package com.xbongbong.saas.help;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.i18n.util.I18nMessageUtil;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.constant.TimeConstant;
import com.xbongbong.paas.domain.entity.PaasFormDataEntity;
import com.xbongbong.paas.domain.entity.PaasFormExplainEntity;
import com.xbongbong.paas.domain.entity.ext.ConditionsEntityExt;
import com.xbongbong.paas.domain.entity.ext.FieldPermissionEntityExt;
import com.xbongbong.paas.domain.entity.ext.PaasFormDataEntityExt;
import com.xbongbong.paas.domain.entity.ext.PaasFormEntityExt;
import com.xbongbong.paas.elasticsearch.model.PaasFormDataEsModel;
import com.xbongbong.paas.elasticsearch.pojo.XbbAggregatedPage;
import com.xbongbong.paas.elasticsearch.util.EsHelper;
import com.xbongbong.paas.elasticsearch.util.EsUtil;
import com.xbongbong.paas.elasticsearch.util.XbbElasticsearchRestTemplate;
import com.xbongbong.paas.enums.ConditionEnum;
import com.xbongbong.paas.enums.DateTimeEnum;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.enums.FieldTypeEnum;
import com.xbongbong.paas.enums.FlowStatusEnum;
import com.xbongbong.paas.enums.FundAccountFlowTypeEnum;
import com.xbongbong.paas.enums.IndexTypeEnum;
import com.xbongbong.paas.enums.OtherExpenseSheetTypeEnum;
import com.xbongbong.paas.enums.OtherIncomeSheetTypeEnum;
import com.xbongbong.paas.enums.PaySheetTypeEnum;
import com.xbongbong.paas.enums.PaymentSheetTypeEnum;
import com.xbongbong.paas.enums.SheetTypeEnum;
import com.xbongbong.paas.field.FieldAttrEntity;
import com.xbongbong.paas.field.ItemPoJo;
import com.xbongbong.paas.help.CommonHelp;
import com.xbongbong.paas.model.PaasFormExplainModel;
import com.xbongbong.paas.pojo.IdNamePojo;
import com.xbongbong.paas.pojo.ShareConditionFormatPojo;
import com.xbongbong.paas.pojo.dto.FormDataListDTO;
import com.xbongbong.paas.pojo.vo.PaasFormDataEsListVO;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.Arith;
import com.xbongbong.paas.toolbox.util.BeanUtil;
import com.xbongbong.paas.toolbox.util.CollectionsUtil;
import com.xbongbong.paas.toolbox.util.DateTimeUtil;
import com.xbongbong.paas.toolbox.util.JsonHelperUtil;
import com.xbongbong.paas.toolbox.util.StringUtil;
import com.xbongbong.pro.constant.ElasticConstant;
import com.xbongbong.pro.constant.I18nStringConstant;
import com.xbongbong.pro.constant.SymbolConstant;
import com.xbongbong.pro.customerstatement.pojo.StatementCustomerPojo;
import com.xbongbong.pro.detailtab.dto.DetailTabDataGetDTO;
import com.xbongbong.pro.enums.DistributorMarkEnum;
import com.xbongbong.pro.enums.SaasMarkEnum;
import com.xbongbong.pro.enums.errorcodes.StatementErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.StatisticsServicesErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.SystemErrorCodeEnum;
import com.xbongbong.pro.enums.fund.enums.PayMethodDistributorEnum;
import com.xbongbong.pro.formdata.pojo.LinkBusinessSinglePojo;
import com.xbongbong.pro.label.pojo.LabelPojo;
import com.xbongbong.pro.sheet.pojo.AmountDetailGetPojo;
import com.xbongbong.pro.sheet.pojo.SheetAttrPojo;
import com.xbongbong.pro.sheet.pojo.vo.SheetAmountDetailGetVO;
import com.xbongbong.pro.statistic.constant.FundStatisticsConstant;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.constant.InvoiceConstant;
import com.xbongbong.saas.constant.ParameterConstant;
import com.xbongbong.saas.constant.StringConstant;
import com.xbongbong.saas.domain.entity.CustomerStatementEntity;
import com.xbongbong.saas.domain.entity.CustomerStatementSnapshotEntity;
import com.xbongbong.saas.domain.entity.FundAccountFlowEntity;
import com.xbongbong.saas.domain.entity.ext.PaymentEntityExt;
import com.xbongbong.saas.enums.CustomerStatementEnum;
import com.xbongbong.saas.enums.CustomerStatementSnapshotEnum;
import com.xbongbong.saas.enums.DefaultDistributorProcessEnum;
import com.xbongbong.saas.enums.SheetAloneEnum;
import com.xbongbong.saas.enums.SheetAssembleEnum;
import com.xbongbong.saas.enums.ShowTypeEnum;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.enums.base.PaymentSheetBaseEnum;
import com.xbongbong.saas.enums.business.CreditFixedLimitEnum;
import com.xbongbong.saas.enums.business.CreditTemporaryLimitEnum;
import com.xbongbong.saas.enums.business.InvoiceDistributorEnum;
import com.xbongbong.saas.enums.business.InvoiceEnum;
import com.xbongbong.saas.enums.business.OtherExpenseEnum;
import com.xbongbong.saas.enums.business.OtherIncomeEnum;
import com.xbongbong.saas.enums.business.PaySheetEnum;
import com.xbongbong.saas.enums.business.PaymentEnum;
import com.xbongbong.saas.enums.business.PaymentSheetDistributorEnum;
import com.xbongbong.saas.enums.business.PaymentSheetEnum;
import com.xbongbong.saas.enums.business.PurchaseInvoiceEnum;
import com.xbongbong.saas.enums.business.ReturnEnum;
import com.xbongbong.saas.model.DataDictionaryModel;
import com.xbongbong.saas.model.FundAccountFlowModel;
import com.xbongbong.saas.model.FundAccountModel;
import com.xbongbong.saas.model.IndexTypeModel;
import com.xbongbong.saas.service.toolbox.help.SaasListDataPermissionHelp;
import com.xbongbong.saas.service.toolbox.statistic.help.ChartHelp;
import com.xbongbong.saas.toolbox.help.FastJsonHelper;
import com.xbongbong.saas.util.FundUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import static com.xbongbong.saas.toolbox.help.FastJsonHelper.getJsonArrFromFormData;
import static com.xbongbong.saas.toolbox.help.FastJsonHelper.getJsonArrOrDefaultFromFormData;
import static java.util.stream.Collectors.toList;
import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
import static org.elasticsearch.index.query.QueryBuilders.termsQuery;

/**
 * 资金相关公共方法
 * @author zcp
 * @version v1.0
 * @since v1.0
 */
@Component
public class FundHelp {

    private static final Logger LOG = LoggerFactory.getLogger(FundHelp.class);

    @Resource
    private XbbElasticsearchRestTemplate xbbElasticsearchRestTemplate;
    @Resource
    private PaasFormDataEsModel paasFormDataEsModel;
    @Resource
    private DataDictionaryModel dataDictionaryModel;
    @Resource
    private FundAccountModel fundAccountModel;
    @Resource
    private ChartHelp chartHelp;
    @Resource
    private CommonHelp commonHelp;
    @Resource
    private EsHelper esHelper;
    @Resource
    private SaasFundHelp saasFundHelp;
    @Resource
    private PaasFormExplainModel paasFormExplainModel;
    @Resource
    private IndexTypeModel indexTypeModel;
    @Resource
    private FundAccountFlowModel fundAccountFlowModel;

    public void handProcessShouldRemoveValue(String corpid, XbbRefTypeEnum xbbRefTypeEnum, XbbRefTypeEnum linkXbbRefTypeEnum, JSONArray linkArray) throws XbbException {
        if (XbbRefTypeEnum.CONTRACT.equals(xbbRefTypeEnum) || XbbRefTypeEnum.PURCHASE.equals(xbbRefTypeEnum)) {
            //把id提取出来
            List<Long> checkIdList = new ArrayList<>();
            //审批里面需要移除的id
            List<Long> shouldRemoveIdList = new ArrayList<>();
            for (int i = 0; i < linkArray.size(); i++) {
                Long id = linkArray.getJSONObject(i).getLong(BasicConstant.ID);
                if (Objects.nonNull(id)) {
                    checkIdList.add(id);
                }
            }
            String index = "";
            String type = "";
            switch (linkXbbRefTypeEnum) {
                case PAYMENT_SHEET:
                case WRITE_OFF_PREPAYMENT:
                    index = IndexTypeEnum.IDX_SAAS_PAYMENT_SHEET.getIndex();
                    type = IndexTypeEnum.IDX_SAAS_PAYMENT_SHEET.getType();
                    break;
                case PAYMENT:
                    index = IndexTypeEnum.IDX_SAAS_PAYMENT.getIndex();
                    type = IndexTypeEnum.IDX_SAAS_PAYMENT.getType();
                    break;
                case PAY_SHEET:
                case WRITE_OFF_PREPAY:
                    index = IndexTypeEnum.IDX_SAAS_PAYPLAN_SHEET.getIndex();
                    type = IndexTypeEnum.IDX_SAAS_PAYPLAN_SHEET.getType();
                    break;
                case PAY_PLAN:
                    index = IndexTypeEnum.IDX_SAAS_PAY_PLAN.getIndex();
                    type = IndexTypeEnum.IDX_SAAS_PAY_PLAN.getType();
                    break;
                default:
                    break;
            }
            if (CollectionsUtil.isNotEmpty(checkIdList)) {
                XbbAggregatedPage<PaasFormDataEntity>  xbbAggregatedPage = getEsDataByIdList(corpid, checkIdList, index, type);
                if (Objects.nonNull(xbbAggregatedPage)) {
                    List<PaasFormDataEntity> paasFormDataEntities = xbbAggregatedPage.getContent();
                    shouldRemoveIdList = paasFormDataEntities.stream().map(paasFormDataEntity -> paasFormDataEntity.getId()).collect(toList());
                }
                if (CollectionsUtil.isNotEmpty(shouldRemoveIdList)) {
                    Iterator<Object> iterator = linkArray.iterator();
                    while (iterator.hasNext()) {
                        JSONObject shouldRemoveObj = (JSONObject)iterator.next();
                        if (shouldRemoveIdList.contains(shouldRemoveObj.getLong(BasicConstant.ID))) {
                            iterator.remove();
                        }
                    }
                }
            }

        }
    }

    /**
     * 一个共用的es查询方法
     *
     * @param corpid 公司id
     * @param checkIdList 需要查询的id列表
     * @param index 索引
     * @param type 类型
     * @return
     * @throws XbbException
     */
    private XbbAggregatedPage<PaasFormDataEntity> getEsDataByIdList(String corpid, List<Long> checkIdList, String index, String type) throws XbbException {
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
        boolQueryBuilder.filter(termQuery(StringConstant.DEL, DelEnum.DELETE.getDel()));
        boolQueryBuilder.filter(termsQuery(BasicConstant.ID, checkIdList));
        boolQueryBuilder.filter(termQuery(ElasticConstant.MY_JOIN_FIELD, type));
        searchSourceBuilder.query(boolQueryBuilder);
        SearchRequest searchRequest = new SearchRequest(index);
        searchRequest.source(searchSourceBuilder);
        PageRequest pageRequest = EsUtil.setPage(searchSourceBuilder, 1, 10000);
        XbbAggregatedPage<PaasFormDataEntity> esEntities = xbbElasticsearchRestTemplate.queryForPages(pageRequest, searchRequest, PaasFormDataEntity.class);
        return esEntities;
    }

    /**
     * 获取回/付款单的金额：（已经确定是哪种类型回/付款单）
     * 【注：如果能确定是哪种业务，则不建议使用该方法，可以 直接分别调用getSheetAmount4PaymentSheet、getSheetAmount4PaySheet】
     * @param sheetTypeEnum 已经确定是哪种类型回/付款单
     * @param data 回/付款的实体data
     * @return double 回/付款金额 or 核销金额
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public static double getSheetAmountBySheetType(SheetTypeEnum sheetTypeEnum, JSONObject data) throws XbbException {
        return FundUtil.getSheetAmountBySheetType(sheetTypeEnum, data);
    }
    /**
     * 获取回/付款单的金额：（不确定是哪种类型回/付款单，需要读取data内的回/付款类型）
     * 【注：如果能确定是哪种业务，则不建议使用该方法，可以 直接分别调用getSheetAmount4PaymentSheet、getSheetAmount4PaySheet】
     * @param businessType 如果是获取回款单回款金额(核销金额)，则传 XbbRefTypeEnum.PAYMENT_SHEET；如果是获取付款单的付款金额(核销金额)，则传XbbRefTypeEnum.PAY_SHEET
     * @param data 回/付款的实体data
     * @return double 回/付款金额 or 核销金额
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public double getSheetAmountByBusinessType(Integer businessType, JSONObject data) throws XbbException {
        if (data == null) {
            return 0;
        }
        if (Objects.equals(businessType, XbbRefTypeEnum.PAY_SHEET.getCode())) {
            //付款金额or核销金额
            String paySheetType = data.getString(PaySheetEnum.PAY_SHEET_TYPE.getAttr());
            return getSheetAmount4PaySheet(data, paySheetType);
        } else {
            //回款金额or核销金额
            String paymentSheetType = data.getString(PaymentSheetEnum.PAYMENT_SHEET_TYPE.getAttr());
            return getSheetAmount4PaymentSheet(data, paymentSheetType);
        }
    }
    /**
     * 得到回款单的回款金額（核销金额）
     * @param data 回款单data
     * @param paymentSheetType 回款单类型
     * @return double
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public static double getSheetAmount4PaymentSheet(JSONObject data, String paymentSheetType) {
        return FundUtil.getSheetAmount4PaymentSheet(data, paymentSheetType);
    }

    /**
     * 得到付款单的付款金額（核销金额）
     * @param data 付款单data
     * @param paySheetType 付款单类型
     * @return double
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public static double getSheetAmount4PaySheet(JSONObject data, String paySheetType) throws XbbException {
        return FundUtil.getSheetAmount4PaySheet(data, paySheetType);
    }


    /**
     * 通过父的回/付款单uuid（alone=1、2的回款单），查询alone=SheetAloneEnum.CHILD的回/付款单（明细）list
     * 只获取子回/付款单，如果alone的值是PaymentSheetAloneEnum.NORMAL，则其本身就是自己的明细，无需查询明细
     * @param indexTypeEnum 业务es索引枚举
     * @param corpid 公司id
     * @param uuid 回/付款单uuid（父的回/付款单的uuid）
     * @param orderStr 需要排序的字段
     * @return java.util.List<com.xbongbong.paas.domain.entity.ext.PaasFormDataEntityExt>
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v2.0
     * @date 19/11/18 018 13:54
     * @update 2020/2/20 从PaymentSheetService移入，且改造使其支持付款单查询
     */
    public List<PaasFormDataEntityExt> getChildEsDataListByParentUuid(IndexTypeEnum indexTypeEnum, String corpid, String uuid, String orderStr) throws XbbException {
        return getEsDataListByUuidAndAlone(indexTypeEnum, corpid, SheetAloneEnum.CHILD, uuid, orderStr);
    }
    /**
     * 根据uuid、alone获取回款单
     * @param indexTypeEnum 业务es索引枚举
     * @param corpid 公司id
     * @param sheetAloneEnum 要查询的父子类型
     * @param uuid 回/付款单uuid（父的回/付款单的uuid）
     * @param orderStr 需要排序的字段
     * @return java.util.List<com.xbongbong.paas.domain.entity.ext.PaasFormDataEntityExt>
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public List<PaasFormDataEntityExt> getEsDataListByUuidAndAlone(IndexTypeEnum indexTypeEnum, String corpid, SheetAloneEnum sheetAloneEnum, String uuid, String orderStr) throws XbbException {
        return saasFundHelp.getEsDataListByUuidAndAlone(indexTypeEnum, corpid, sheetAloneEnum, uuid, orderStr);
    }

    /**
     * 通过父的回/付款单uuid（alone=1、2的回/付款单），查询alone=SheetAloneEnum.CHILD的回/付款单（明细）list
     * 只获取子回/付款单，如果alone的值是PaymentSheetAloneEnum.NORMAL，则其本身就是自己的明细，无需查询明细
     *
     * @param xbbRefTypeEnum 业务类型
     * @param corpid 公司id
     * @param uuids 回/付款单uuid集合（多个父回/付款单uuid），注：需防止出现[null, "xxx"]的情况
     * @param linkId 关联id，如果不限制关联字段条件则传null
     * @param linkId 需要查询的关联字段，如果不限制关联字段条件则传null
     * @return java.util.List<com.xbongbong.paas.domain.entity.ext.PaasFormDataEntityExt>
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v2.0
     * @date 19/11/18 018 14:53
     * @update 2020/2/20 从PaymentSheetService移入，且改造使其支持付款单查询
     */
    public List<PaasFormDataEntityExt> getChildEsDataListByParentUuids(XbbRefTypeEnum xbbRefTypeEnum, String corpid, Collection uuids, Long linkId, String linkAttr) throws XbbException {
        if (uuids == null || uuids.size() == 0 || CollectionsUtil.isEmpty(uuids)) {
            return new ArrayList<>();
        }
        //防止出现[null, "xxx"]的情况
        uuids.remove(null);
        uuids.remove("null");
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
        boolQueryBuilder.filter(termsQuery(FieldTypeEnum.SHEET_UUID.getAlias() + ".keyword", uuids));
        boolQueryBuilder.filter(termQuery("del", 0));
        //只获取子回款单，如果alone的值是PaymentSheetAloneEnum.NORMAL，则其本身就是自己的明细，无需查询明细
        boolQueryBuilder.filter(termQuery(FieldTypeEnum.SHEET_ALONE.getAlias(), SheetAloneEnum.CHILD.getCode()));
        IndexTypeEnum indexTypeEnum;
        switch (xbbRefTypeEnum) {
            case PAYMENT_SHEET:
                indexTypeEnum = IndexTypeEnum.IDX_SAAS_PAYMENT_SHEET;
                break;
            case PAY_SHEET:
                indexTypeEnum = IndexTypeEnum.IDX_SAAS_PAYPLAN_SHEET;
                break;
            default:
                //默认为回款单
                indexTypeEnum = IndexTypeEnum.IDX_SAAS_PAYPLAN_SHEET;
                break;
        }
        FormDataListDTO formDataListDTO = new FormDataListDTO();
        formDataListDTO.setCorpid(corpid);
        formDataListDTO.setBusinessType(xbbRefTypeEnum.getCode());
        //处理关联字段条件
        List<ConditionsEntityExt> linkConditions = handleLinkCondition(xbbRefTypeEnum, linkId, linkAttr);
        //解析查询条件
        EsUtil.parseCondition(boolQueryBuilder, linkConditions, formDataListDTO.getRel(), formDataListDTO.getCorpid(), formDataListDTO.getBusinessType());
        boolQueryBuilder.filter(termQuery(ElasticConstant.MY_JOIN_FIELD,indexTypeEnum.getType()));
        sourceBuilder.query(boolQueryBuilder);
        SearchRequest searchRequest = new SearchRequest(indexTypeEnum.getIndex());
        PageRequest pageRequest = EsUtil.setPage(sourceBuilder, 0, PaasConstant.ES_MAX_PAGE_SIZE);
        searchRequest.source(sourceBuilder);
        XbbAggregatedPage<PaasFormDataEntityExt> esEntities = xbbElasticsearchRestTemplate.queryForPages(pageRequest, searchRequest, PaasFormDataEntityExt.class);
        return esEntities.getContent();
    }

    /**
     * 通过子的回/付款单uuid，查询alone=SheetAloneEnum.Parent、CHILD（明细）list
     * @param indexTypeEnum 业务es索引枚举
     * @param corpid
     * @param uuids 注：需防止出现[null, "xxx"]的情况
     * @return java.util.List<com.xbongbong.paas.domain.entity.ext.PaasFormDataEntityExt>
     * @throws XbbException
     * @author yuqian
     * @date 19/12/15 21:53
     * @since v1.0
     * @version v2.0
     * @update 2020/2/20 从PaymentSheetService移入，且改造使其支持付款单查询
     */
    public List<PaasFormDataEntityExt> getParentAndChildEsDataListByUuids(IndexTypeEnum indexTypeEnum, String corpid, Collection uuids) throws XbbException {
        if (uuids == null || uuids.size() == 0) {
            return new ArrayList<>();
        }
        //防止出现[null, "xxx"]的情况
        uuids.remove(null);
        uuids.remove("null");
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
        boolQueryBuilder.filter(termsQuery(FieldTypeEnum.SHEET_UUID.getAlias() + ".keyword", uuids));
        boolQueryBuilder.filter(termQuery("del", 0));
        //只获取子回款单，如果alone的值是PaymentSheetAloneEnum.NORMAL，则其本身就是自己的明细，无需查询明细
        List<Integer> aloneList = new ArrayList<>();
        aloneList.add(SheetAloneEnum.PARENT.getCode());
        aloneList.add(SheetAloneEnum.CHILD.getCode());
        boolQueryBuilder.filter(termsQuery(FieldTypeEnum.SHEET_ALONE.getAlias(), aloneList));
        boolQueryBuilder.filter(termQuery(ElasticConstant.MY_JOIN_FIELD,indexTypeEnum.getType()));
        sourceBuilder.query(boolQueryBuilder);
        SearchRequest searchRequest = new SearchRequest(indexTypeEnum.getIndex());
        PageRequest pageRequest = EsUtil.setPage(sourceBuilder, 0, PaasConstant.ES_MAX_PAGE_SIZE);
        searchRequest.source(sourceBuilder);
        XbbAggregatedPage<PaasFormDataEntityExt> esEntities = xbbElasticsearchRestTemplate.queryForPages(pageRequest, searchRequest, PaasFormDataEntityExt.class);
        return esEntities.getContent();
    }

    /**
     * 根据关联id获取回款单/付款单/其他收入单/其他支出单list
     *
     * @param xbbRefTypeEnum      业务类型
     * @param detailTabDataGetDTO 入参
     * @param linkId              关联id
     * @param attr                linkId对应的字段名
     * @param needFilterApproval  是否需要过滤审批数据
     * @param isRed               当传null，则不限制红冲，否则1读取红冲类型的回款单、付款单、其他收入、其他支出，0非红冲类型回款单、付款单、其他收入、其他支出（如红冲记录，只获取红冲的）
     * @return 数据
     * @throws XbbException 异常
     * @update 2019/12/2 by zcp 增加isRed相关逻辑，支持查询红冲的条件封装
     * @update 2020/3/1 by zcp 从SaasDetailTabService移入，并支持付款单的相关查询
     */
    public PaasFormDataEsListVO getSheetEsListVoByLinkId(XbbRefTypeEnum xbbRefTypeEnum, DetailTabDataGetDTO detailTabDataGetDTO, Long linkId, String attr, boolean needFilterApproval, Integer isRed) throws XbbException {
        PaasFormDataEsListVO paasFormDataEsListVO;
        FormDataListDTO formDataListDTO = new FormDataListDTO();
        BeanUtil.copyProperties(detailTabDataGetDTO, formDataListDTO);
        ShareConditionFormatPojo shareConditionFormatPojo = new ShareConditionFormatPojo();
        shareConditionFormatPojo.setPermissionConditions(detailTabDataGetDTO.getPermissionConditions());
        formDataListDTO.setSharePermission(shareConditionFormatPojo);
        formDataListDTO.setFormId(null);
        List<ConditionsEntityExt> conditionsEntityExtList = detailTabDataGetDTO.getConditions();
        if (CollectionsUtil.isEmpty(conditionsEntityExtList)) {
            conditionsEntityExtList = new ArrayList<>();
        }
        String symbol = ConditionEnum.EQUAL.getSymbol();
        IndexTypeEnum indexTypeEnum = IndexTypeEnum.IDX_SAAS_PAYMENT_SHEET;
        switch (xbbRefTypeEnum) {
            case PAYMENT_SHEET:
                if (Objects.equals(PaymentSheetEnum.CONTRACT.getAttr(), attr) || Objects.equals(PaymentSheetEnum.PAYMENT.getAttr(), attr)
                        || Objects.equals(PaymentSheetEnum.PAYMENT_TASK.getAttr(), attr)) {
                    symbol = ConditionEnum.EQUAL.getSymbol();
                }
                break;
            case PAY_SHEET:
                indexTypeEnum = IndexTypeEnum.IDX_SAAS_PAYPLAN_SHEET;
                if (Objects.equals(PaySheetEnum.LINK_PURCHASE.getAttr(), attr) || Objects.equals(PaySheetEnum.PAY_PLAN.getAttr(), attr)) {
                    symbol = ConditionEnum.EQUAL.getSymbol();
                }
                break;
            case OTHER_INCOME:
                indexTypeEnum = IndexTypeEnum.IDX_SAAS_OTHER_INCOME;
                break;
            case OTHER_EXPENSE:
                indexTypeEnum = IndexTypeEnum.IDX_SAAS_OTHER_EXPENSE;
                break;
            default:
                break;
        }
        ConditionsEntityExt conditionsEntityExt = new ConditionsEntityExt();
        conditionsEntityExt.setSymbol(symbol);
        conditionsEntityExt.setAttr(attr);
        conditionsEntityExt.setValue(Collections.singletonList(linkId));
        conditionsEntityExt.setFieldType(FieldTypeEnum.TEXT.getType());
        conditionsEntityExtList.add(conditionsEntityExt);
        formDataListDTO.setConditions(conditionsEntityExtList);
        formDataListDTO.setBusinessType(xbbRefTypeEnum.getCode());
        if (needFilterApproval) {
            formDataListDTO.setFlowStatusIn(Arrays.asList(FlowStatusEnum.NOT_NEED_APPROVAL.getType(), FlowStatusEnum.PASS.getType()));
            ConditionsEntityExt flowStatusConditionsEntityExt = new ConditionsEntityExt(FieldTypeEnum.FLOW_STATUS.getAlias(), "", FieldTypeEnum.FLOW_STATUS.getType(), ConditionEnum.IN.getSymbol(), Arrays.asList(FlowStatusEnum.NOT_NEED_APPROVAL.getType(), FlowStatusEnum.PASS.getType()));
            conditionsEntityExtList.add(flowStatusConditionsEntityExt);
        }
        handLinkTypeConditions(conditionsEntityExtList,isRed,xbbRefTypeEnum);



        paasFormDataEsListVO = paasFormDataEsModel.sheetList(formDataListDTO, indexTypeEnum);
        return paasFormDataEsListVO;
    }


    /**
     * 处理各种类型回/付款单的字段
     * @param addUnableEdit true 新增是禁止编辑，false 编辑时各类型付款单不能编辑的字段
     * @param sheetTypeEnum 回/付款单类型枚举
     * @param explainList 字段解释list
     * @param distributorMark 经销商标识
     * @return java.util.List<FieldAttrEntity>
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public List<FieldAttrEntity> handlePaymentSheet(boolean addUnableEdit, SheetTypeEnum sheetTypeEnum, List<FieldAttrEntity> explainList, String corpid, Integer distributorMark) {
        //得到新建or编辑时禁止修改的字段list
        List<String> unableEditAttrList;
        switch (sheetTypeEnum.getXbbRefTypeEnum()) {
            case PAY_SHEET:
                unableEditAttrList = PaySheetEnum.getUnableEditAttrList(addUnableEdit, sheetTypeEnum);
                break;
            case OTHER_INCOME:
                unableEditAttrList = OtherIncomeEnum.getUnableEditAttrList(addUnableEdit, sheetTypeEnum);
                break;
            case OTHER_EXPENSE:
                unableEditAttrList = OtherExpenseEnum.getUnableEditAttrList(addUnableEdit, sheetTypeEnum);
                break;
            default:
                unableEditAttrList = PaymentSheetBaseEnum.getUnableEditAttrList(addUnableEdit, sheetTypeEnum, distributorMark);
                break;
        }
        return handlePaymentSheet(unableEditAttrList, sheetTypeEnum, explainList, distributorMark);
    }

    public void handleReceivables(List<FieldAttrEntity> explainList){
        Iterator<FieldAttrEntity> iterator = explainList.iterator();
        List<String> removeAttrList = Arrays.asList(PaymentEnum.STATUS.getAttr(), PaymentEnum.RECEIVABLE_PERIOD.getAttr());
        while (iterator.hasNext()) {
            FieldAttrEntity fieldAttrEntity = iterator.next();
            if(Objects.equals(PaymentEnum.RECEIVABLES_ORIGIN_ID.getAttr(),fieldAttrEntity.getAttr())){
                fieldAttrEntity.setVisible(BasicConstant.ONE);
                fieldAttrEntity.setShowType(ShowTypeEnum.EDIT_DETAIL.getCode());
                fieldAttrEntity.setEditable(BasicConstant.ZERO);
            }
            if(Objects.equals(PaymentEnum.CUSTOMER.getAttr(),fieldAttrEntity.getAttr())){
                fieldAttrEntity.setEditable(BasicConstant.ZERO);
            }
            if(Objects.equals(PaymentEnum.CONTRACT.getAttr(),fieldAttrEntity.getAttr())){
                fieldAttrEntity.setEditable(BasicConstant.ZERO);
            }
            if(Objects.equals(PaymentEnum.SOURCE.getAttr(),fieldAttrEntity.getAttr())){
                fieldAttrEntity.setEditable(BasicConstant.ZERO);
            } else if (Objects.equals(PaymentEnum.AMOUNT.getAttr(), fieldAttrEntity.getAttr())) {
                //应收金额名字变为：红冲应收金额
                fieldAttrEntity.setAttrName(InvoiceConstant.REDSTRING + fieldAttrEntity.getAttrName());
            } else if (removeAttrList.contains(fieldAttrEntity.getAttr())) {
                iterator.remove();
            }
        }
    }

    /**
     * 余乾原方法，不更改其原有意义，只更改实现方式和入参及返回值
     * @param dataIdList 关联新建的数据id集合（应收款的关联新建或退货退款那边的红冲应收款，只可能有一个）
     * @param corpid 公司id
     * @param serialNo 原始应收款编号
     * @return
     * @throws XbbException
     * @since v1.0
     * @version v1.0
     */
    public String handleRedReceivableSerialNo(List<Long> dataIdList,String corpid,String serialNo) throws XbbException{
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
        boolQueryBuilder.filter(termsQuery(PaymentEnum.getEsAttr4Keyword(PaymentEnum.RECEIVABLES_ORIGIN_ID), dataIdList));
        Integer count = chartHelp.getCount4Total(IndexTypeEnum.IDX_SAAS_PAYMENT, boolQueryBuilder);
        return serialNo + StringConstant.POINT + (count + 1);
    }

    public void handleInvoice(List<FieldAttrEntity> explainList) {
        //审批时新建,禁止修改的字段 2020/6/10 暂时封住审批新建不可修改字段
       for(FieldAttrEntity fieldAttrEntity : explainList){
           if(Objects.equals(fieldAttrEntity.getAttr(),InvoiceEnum.CUSTOMER_NAME.getAttr()) ||
                   Objects.equals(fieldAttrEntity.getAttr(), InvoiceEnum.CONTRACT_ID.getAttr()) ||
                   Objects.equals(fieldAttrEntity.getAttr(), InvoiceEnum.PAYMENT_RELATIONSHIP.getAttr()) ||
                   Objects.equals(fieldAttrEntity.getAttr(), InvoiceEnum.PAYMENT_ID.getAttr()) ||
                   Objects.equals(fieldAttrEntity.getAttr(), InvoiceEnum.PAYMENT_SHEET_ID.getAttr())){
               fieldAttrEntity.setEditable(BasicConstant.ZERO);
           }
       }
    }

    /**
     * 获取回/付款单编辑时禁止编辑的字段
     * @param sheetTypeEnum
     * @param distributorMark
     * @return java.util.List<java.lang.String>
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public List<String> getUnableEditAttrList(SheetTypeEnum sheetTypeEnum, Integer distributorMark){
        //得到新建or编辑时禁止修改的字段list
        List<String> unableEditAttrList;
        switch (sheetTypeEnum.getXbbRefTypeEnum()) {
            case PAY_SHEET:
                unableEditAttrList = PaySheetEnum.getUnableEditAttrList(false, sheetTypeEnum);
                break;
            case OTHER_INCOME:
                unableEditAttrList = OtherIncomeEnum.getUnableEditAttrList(false, sheetTypeEnum);
                break;
            case OTHER_EXPENSE:
                unableEditAttrList = OtherExpenseEnum.getUnableEditAttrList(false, sheetTypeEnum);
                break;
            default:
                unableEditAttrList = PaymentSheetBaseEnum.getUnableEditAttrList(false, sheetTypeEnum, distributorMark);
                break;
        }
        return unableEditAttrList;
    }

    /**
     * 处理各种类型回/付款单的字段
     * @param addUnableEdit true 新增是禁止编辑，false 编辑时各类型回/付款单不能编辑的字段
     * @param sheetTypeEnum 回/付款单类型枚举
     * @param explainMap 字段解释map：attr-FieldAttrEntity
     * @param distributorMark 经销商标识
     * @return java.util.Map<java.lang.String,FieldAttrEntity>
     * @author zcp
     * @date 19/11/19 019 15:02
     * @update 19/11/19 019 15:02
     * @since v1.0
     * @version v1.0
     */
    public Map<String, FieldAttrEntity> handlePaymentSheet4ExplainMap(boolean addUnableEdit, SheetTypeEnum sheetTypeEnum, Map<String, FieldAttrEntity> explainMap, Integer distributorMark) {
        String memo = sheetTypeEnum.getMemo();
        XbbRefTypeEnum xbbRefTypeEnum = sheetTypeEnum.getXbbRefTypeEnum();
        //得到新建or编辑时禁止修改的字段list
        List<String> unableEditAttrList = new ArrayList<>();
        //此处因为是显示，所以要展示所有的回/付款单/其他收入单/其他支出单类型
        List<ItemPoJo> itemList = new ArrayList<>();
        String sheetTypeAttr = "";
        switch (xbbRefTypeEnum) {
            case PAY_SHEET:
                unableEditAttrList = PaySheetEnum.getUnableEditAttrList(addUnableEdit, sheetTypeEnum);
                itemList = dataDictionaryModel.getDictionaryList4PaySheetType(true);
                sheetTypeAttr = PaySheetEnum.PAY_SHEET_TYPE.getAttr();
                break;
            case PAYMENT_SHEET:
                unableEditAttrList = PaymentSheetBaseEnum.getUnableEditAttrList(addUnableEdit, sheetTypeEnum, distributorMark);
                itemList = dataDictionaryModel.getDictionaryList4PaymentSheetType(true);
                sheetTypeAttr = PaymentSheetEnum.PAYMENT_SHEET_TYPE.getAttr();
                break;
            case OTHER_INCOME:
                unableEditAttrList = OtherIncomeEnum.getUnableEditAttrList(addUnableEdit, sheetTypeEnum);
                itemList = dataDictionaryModel.getDictionaryList4OtherIncomeSheetType();
                sheetTypeAttr = OtherIncomeEnum.SHEET_TYPE_OTHER.getAttr();
                break;
            case OTHER_EXPENSE:
                unableEditAttrList = OtherExpenseEnum.getUnableEditAttrList(addUnableEdit, sheetTypeEnum);
                itemList = dataDictionaryModel.getDictionaryList4OtherExpenseSheetType();
                sheetTypeAttr = OtherExpenseEnum.SHEET_TYPE_OTHER.getAttr();
                break;
            default:
                unableEditAttrList = PaymentSheetBaseEnum.getUnableEditAttrList(addUnableEdit, sheetTypeEnum, distributorMark);
                itemList = dataDictionaryModel.getDictionaryList4PaymentSheetType(true);
                sheetTypeAttr = PaymentSheetEnum.PAYMENT_SHEET_TYPE.getAttr();
                break;
        }
        switch (sheetTypeEnum) {
            case WRITE_OFF:
            case WRITE_OFF_PAY:
            case OTHER_INCOME:
            case OTHER_EXPENSE:
                //编辑时，部分字段需要禁止修改
                for (Map.Entry<String, FieldAttrEntity> entry : explainMap.entrySet()) {
                    FieldAttrEntity fieldAttrEntity = entry.getValue();
                    handleAttr4Editable(unableEditAttrList, fieldAttrEntity, fieldAttrEntity.getAttr(), sheetTypeAttr, memo, itemList);
                }
                break;
            case WRITE_OFF_PREPAYMENT:
            case WRITE_OFF_PREPAY:
                //预收/付款核销要移除的字段，除了要移除部分系统字段，还需要【移除自定义字段】
                List<String> removeAttrList = SheetAssembleEnum.WriteOffPrepayment.removeAttr(xbbRefTypeEnum);
                //attr-要更新的字段枚举
                Map<String, SheetAssembleEnum.WriteOffPrepayment> updateMap = SheetAssembleEnum.WriteOffPrepayment.updateAttr(xbbRefTypeEnum);
                Iterator<Map.Entry<String, FieldAttrEntity>> iterator = explainMap.entrySet().iterator();
                while (iterator.hasNext()) {
                    Map.Entry<String, FieldAttrEntity> entry = iterator.next();
                    FieldAttrEntity fieldAttrEntity = entry.getValue();
                    handleAttr4WriteOffPrepayment(xbbRefTypeEnum, unableEditAttrList, removeAttrList, updateMap, iterator, fieldAttrEntity, memo, itemList);
                }
                break;
            case PREPAYMENT:
            case PREPAY:
                //预收/付款要移除的字段
                removeAttrList = SheetAssembleEnum.Prepayment.removeAttr(xbbRefTypeEnum);
                iterator = explainMap.entrySet().iterator();
                while (iterator.hasNext()) {
                    Map.Entry<String, FieldAttrEntity> entry = iterator.next();
                    FieldAttrEntity fieldAttrEntity = entry.getValue();
                    //预收/付款：移除三个系统字段
                    handleAttr4Prepayment(xbbRefTypeEnum, unableEditAttrList, removeAttrList, iterator, fieldAttrEntity, memo, itemList);
                }
                break;
            case RED_WRITE_OFF:
            case RED_WRITE_OFF_PREPAYMENT:
            case RED_WRITE_OFF_PAY:
            case RED_WRITE_OFF_PREPAY:
                //红冲已核销、红冲预收/付款核销要移除的字段
                removeAttrList = SheetAssembleEnum.RedWriteOff.removeAttr(xbbRefTypeEnum);
                //attr-要更新的字段枚举
                Map<String, SheetAssembleEnum.RedWriteOff> redUpdateMap = SheetAssembleEnum.RedWriteOff.updateAttr(xbbRefTypeEnum);
                iterator = explainMap.entrySet().iterator();
                while (iterator.hasNext()) {
                    Map.Entry<String, FieldAttrEntity> entry = iterator.next();
                    FieldAttrEntity fieldAttrEntity = entry.getValue();
                    handleAttr4RedWriteOff(xbbRefTypeEnum, unableEditAttrList, removeAttrList, iterator, redUpdateMap, fieldAttrEntity, memo, itemList);
                }
                break;
            case RED_WRITE_OFF_BALANCE:
                //红冲预收款核销(退到余额)要移除的字段
                removeAttrList = SheetAssembleEnum.RedWriteOffBalance.removeAttr(xbbRefTypeEnum);
                //attr-要更新的字段枚举
                Map<String, SheetAssembleEnum.RedWriteOffBalance> redWriteOffBalanceMap = SheetAssembleEnum.RedWriteOffBalance.updateAttr(xbbRefTypeEnum);
                iterator = explainMap.entrySet().iterator();
                while (iterator.hasNext()) {
                    Map.Entry<String, FieldAttrEntity> entry = iterator.next();
                    FieldAttrEntity fieldAttrEntity = entry.getValue();
                    handleAttr4RedWriteOffBalance(xbbRefTypeEnum, unableEditAttrList, removeAttrList, redWriteOffBalanceMap, iterator, fieldAttrEntity, memo, itemList);
                }
                break;
            case RED_PREPAYMENT:
            case RED_PREPAY:
                //红冲预收/付款要移除的字段
                removeAttrList = SheetAssembleEnum.RedPrepayment.removeAttr(xbbRefTypeEnum);
                //attr-要更新的字段枚举
                Map<String, SheetAssembleEnum.RedPrepayment> redPrepaymentUpdateMap = SheetAssembleEnum.RedPrepayment.updateAttr(xbbRefTypeEnum);
                iterator = explainMap.entrySet().iterator();
                while (iterator.hasNext()) {
                    Map.Entry<String, FieldAttrEntity> entry = iterator.next();
                    FieldAttrEntity fieldAttrEntity = entry.getValue();
                    handleAttr4RedPrepayment(xbbRefTypeEnum, unableEditAttrList, removeAttrList, iterator, redPrepaymentUpdateMap, fieldAttrEntity, memo, itemList);
                }
                break;
            case BAD_PAYMENT:
            case BAD_PAY:
                //坏账要移除的字段
                removeAttrList = SheetAssembleEnum.BadPayment.removeAttr(xbbRefTypeEnum);
                //attr-要更新的字段枚举
                Map<String, SheetAssembleEnum.BadPayment> badUpdateMap = SheetAssembleEnum.BadPayment.updateAttr(xbbRefTypeEnum);
                iterator = explainMap.entrySet().iterator();
                while (iterator.hasNext()) {
                    Map.Entry<String, FieldAttrEntity> entry = iterator.next();
                    FieldAttrEntity fieldAttrEntity = entry.getValue();
                    handleAttr4BadPayment(xbbRefTypeEnum, unableEditAttrList, removeAttrList, iterator, badUpdateMap, fieldAttrEntity, memo, itemList);
                }
                break;
            case RED_OTHER_INCOME:
            case RED_OTHER_EXPENSE:
                //红冲其他收入单/其他支出单，即红冲已收款/红冲已付款
                //attr-要更新的字段枚举
                Map<String, SheetAssembleEnum.RedOther> redOtherUpdateMap = SheetAssembleEnum.RedOther.updateAttr(xbbRefTypeEnum);
                iterator = explainMap.entrySet().iterator();
                while (iterator.hasNext()) {
                    Map.Entry<String, FieldAttrEntity> entry = iterator.next();
                    FieldAttrEntity fieldAttrEntity = entry.getValue();
                    handleAttr4RedOther(xbbRefTypeEnum, unableEditAttrList, redOtherUpdateMap, fieldAttrEntity, memo, itemList);
                }
                break;
            default:
                break;
        }
        return explainMap;
    }

    /**
     * 删除不在mapping中的字段：子表单升级后，data内字段有严格校验，非表单字段不能插入es
     * @param data
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public void removeNotInMapping(JSONObject data) {
        if (Objects.nonNull(data)) {
            data.remove(PaasConstant.REFUND_ID);
            data.remove(PaasConstant.RETURNED_PURCHASE_ID);
            data.remove(FieldTypeEnum.ADDTIME.getAlias());
            data.remove(FieldTypeEnum.UPDATETIME.getAlias());
            data.remove(FieldTypeEnum.CREATORID.getAlias());
            data.remove(FieldTypeEnum.OWNERID.getAlias());
            data.remove(FieldTypeEnum.COUSERID.getAlias());
            data.remove(FieldTypeEnum.SERIALNO.getAlias());
            data.remove(FieldTypeEnum.DEPARTMENTID.getAlias());
        }
    }
    /**
     * 删除不在mapping中的字段：子表单升级后，data内字段有严格校验，非表单字段不能插入es
     * @param dataArray 数组，比如明细内数据
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public void removeNotInMapping4Array(JSONArray dataArray) {
        if (Objects.nonNull(dataArray)) {
            for(Object obj : dataArray) {
                JSONObject jsonObject = (JSONObject) obj;
                removeNotInMapping(jsonObject);
            }
        }
    }

    /**
     * 处理编辑时候的回/付款单data：禁止编辑的字段还使用旧值，一些类型没有的字段，也不会给它增加值（防止导入覆盖时放入新值）
     * @param sheetTypeEnum 回/付款单类型
     * @param data 新的回/付款单data
     * @param oldData 旧回/付款单data
     * @param distributorMark 经销商标识
     * @return com.alibaba.fastjson.JSONObject
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public JSONObject handlePaymentSheetData4Update(SheetTypeEnum sheetTypeEnum, JSONObject data, JSONObject oldData, Integer distributorMark, Map<String, FieldAttrEntity> explainMap) throws XbbException {
        data.remove(PaasConstant.AMOUNTDETAIL);
        //remove mapping中不存在的字段， 兼容老逻辑
        data.remove(PaasConstant.REFUND_ID);
        data.remove(PaasConstant.RETURNED_PURCHASE_ID);
        data.remove("addTime");
        data.remove("updateTime");
        XbbRefTypeEnum xbbRefTypeEnum = sheetTypeEnum.getXbbRefTypeEnum();
        if (Objects.nonNull(explainMap)) {
            dealLabelData(data, explainMap);
        }
        //得到编辑时禁止修改的字段list
        List<String> unableEditAttrList;
        switch (xbbRefTypeEnum) {
            case PAY_SHEET:
                unableEditAttrList = PaySheetEnum.getUnableEditAttrList(false, sheetTypeEnum);
                break;
                //这边对其他支出单和其他收入单处理
            case OTHER_INCOME:
                unableEditAttrList = OtherIncomeEnum.getUnableEditAttrList(false, sheetTypeEnum);
                break;
            case OTHER_EXPENSE:
                unableEditAttrList = OtherExpenseEnum.getUnableEditAttrList(false, sheetTypeEnum);
                break;
            default:
                unableEditAttrList = PaymentSheetBaseEnum.getUnableEditAttrList(false, sheetTypeEnum, distributorMark);
                break;
        }
        List<String> removeAttrList = new ArrayList<>();
        switch (sheetTypeEnum) {
            case WRITE_OFF:
            case WRITE_OFF_PAY:
                break;
            case WRITE_OFF_PREPAYMENT:
            case WRITE_OFF_PREPAY:
                //预收/付款核销要移除的字段，除了要移除部分系统字段，还需要【移除自定义字段】
                removeAttrList = SheetAssembleEnum.WriteOffPrepayment.removeAttr(xbbRefTypeEnum);
                break;
            case PREPAYMENT:
            case PREPAY:
                //预收款要移除的字段
                removeAttrList = SheetAssembleEnum.Prepayment.removeAttr(xbbRefTypeEnum);
                break;
            case RED_WRITE_OFF:
            case RED_WRITE_OFF_PREPAYMENT:
            case RED_WRITE_OFF_PAY:
            case RED_WRITE_OFF_PREPAY:
                //红冲已核销、红冲预收/付款核销要移除的字段
                removeAttrList = SheetAssembleEnum.RedWriteOff.removeAttr(xbbRefTypeEnum);
                break;
            case RED_WRITE_OFF_BALANCE:
                //红冲预收款核销(退到余额)要移除的字段
                removeAttrList = SheetAssembleEnum.RedWriteOffBalance.removeAttr(xbbRefTypeEnum);
                break;
            case RED_PREPAYMENT:
            case RED_PREPAY:
                //红冲预收/付款要移除的字段
                removeAttrList = SheetAssembleEnum.RedPrepayment.removeAttr(xbbRefTypeEnum);
                break;
            case BAD_PAYMENT:
            case BAD_PAY:
                //坏账要移除的字段
                removeAttrList = SheetAssembleEnum.BadPayment.removeAttr(xbbRefTypeEnum);
                break;
            default:
                break;
        }
        for (String attr : unableEditAttrList) {
            data.put(attr, oldData.get(attr));
        }
        for (String attr : removeAttrList) {
            data.remove(attr);
        }
        return data;
    }


    /**
     * 处理编辑时候的回/付款单data：禁止编辑的字段还使用旧值，一些类型没有的字段，也不会给它增加值（防止导入覆盖时放入新值,工作流字段特殊处理
     * @param sheetTypeEnum
     * @param data
     * @param oldData
     * @param distributorMark
     * @param explainMap
     * @author yanglei by handlePaymentSheetData4Update
     * @return
     * @throws XbbException
     */
    public JSONObject handlePaymentSheetData4WorkflowUpdate(SheetTypeEnum sheetTypeEnum, JSONObject data, JSONObject oldData, Integer distributorMark, Map<String, FieldAttrEntity> explainMap) throws XbbException {
        data.remove(PaasConstant.AMOUNTDETAIL);
        //remove mapping中不存在的字段， 兼容老逻辑
        data.remove(PaasConstant.REFUND_ID);
        data.remove(PaasConstant.RETURNED_PURCHASE_ID);
        data.remove("addTime");
        data.remove("updateTime");
        XbbRefTypeEnum xbbRefTypeEnum = sheetTypeEnum.getXbbRefTypeEnum();
        if (Objects.nonNull(explainMap)) {
            dealLabelData(data, explainMap);
        }
        //得到编辑时禁止修改的字段list
        List<String> unableEditAttrList;
        switch (xbbRefTypeEnum) {
            case PAY_SHEET:
                unableEditAttrList = PaySheetEnum.getUnableEditAttrList(false, sheetTypeEnum);
                unableEditAttrList.remove(PaySheetEnum.REAL_PAY_TIME.getAttr());
                unableEditAttrList.remove(PaySheetEnum.FUND_ACCOUNT.getAttr());
                break;
            //这边对其他支出单和其他收入单处理
            case OTHER_INCOME:
                unableEditAttrList = OtherIncomeEnum.getUnableEditAttrList(false, sheetTypeEnum);
                unableEditAttrList.remove(OtherIncomeEnum.INCOME_TIME.getAttr());
                unableEditAttrList.remove(OtherIncomeEnum.SHEET_NO.getAttr());
                unableEditAttrList.remove(OtherIncomeEnum.FUND_ACCOUNT.getAttr());
                break;
            case OTHER_EXPENSE:
                unableEditAttrList = OtherExpenseEnum.getUnableEditAttrList(false, sheetTypeEnum);
                unableEditAttrList.remove(OtherExpenseEnum.EXPENSE_TIME.getAttr());
                unableEditAttrList.remove(OtherExpenseEnum.SHEET_NO.getAttr());
                unableEditAttrList.remove(OtherExpenseEnum.FUND_ACCOUNT.getAttr());
                break;
            default:
                unableEditAttrList = PaymentSheetBaseEnum.getUnableEditAttrList(false, sheetTypeEnum, distributorMark);
                unableEditAttrList.remove(PaymentSheetEnum.PAYMENT_TIME.getAttr());
                unableEditAttrList.remove(PaySheetEnum.FUND_ACCOUNT.getAttr());
                break;
        }
        List<String> removeAttrList = new ArrayList<>();
        switch (sheetTypeEnum) {
            case WRITE_OFF:
            case WRITE_OFF_PAY:
                break;
            case WRITE_OFF_PREPAYMENT:
            case WRITE_OFF_PREPAY:
                //预收/付款核销要移除的字段，除了要移除部分系统字段，还需要【移除自定义字段】
                removeAttrList = SheetAssembleEnum.WriteOffPrepayment.removeAttr(xbbRefTypeEnum);
                break;
            case PREPAYMENT:
            case PREPAY:
                //预收款要移除的字段
                removeAttrList = SheetAssembleEnum.Prepayment.removeAttr(xbbRefTypeEnum);
                break;
            case RED_WRITE_OFF:
            case RED_WRITE_OFF_PREPAYMENT:
            case RED_WRITE_OFF_PAY:
            case RED_WRITE_OFF_PREPAY:
                //红冲已核销、红冲预收/付款核销要移除的字段
                removeAttrList = SheetAssembleEnum.RedWriteOff.removeAttr(xbbRefTypeEnum);
                break;
            case RED_WRITE_OFF_BALANCE:
                //红冲预收款核销(退到余额)要移除的字段
                removeAttrList = SheetAssembleEnum.RedWriteOffBalance.removeAttr(xbbRefTypeEnum);
                break;
            case RED_PREPAYMENT:
            case RED_PREPAY:
                //红冲预收/付款要移除的字段
                removeAttrList = SheetAssembleEnum.RedPrepayment.removeAttr(xbbRefTypeEnum);
                break;
            case BAD_PAYMENT:
            case BAD_PAY:
                //坏账要移除的字段
                removeAttrList = SheetAssembleEnum.BadPayment.removeAttr(xbbRefTypeEnum);
                break;
            default:
                break;
        }
        for (String attr : unableEditAttrList) {
            data.put(attr, oldData.get(attr));
        }
        for (String attr : removeAttrList) {
            data.remove(attr);
        }
        return data;
    }

    /**
     * 标签字段处理
     * @param data
     * @param explainMap
     * @throws XbbException
     */
    public void dealLabelData(JSONObject data,  Map<String, FieldAttrEntity> explainMap) throws XbbException {
        // 标签处理
        String labelAttr;
        for (FieldAttrEntity value : explainMap.values()) {
            if (Objects.equals(value.getFieldType(), FieldTypeEnum.LABEL.getType())){
                labelAttr = value.getAttr();
                JSONArray stringLableData = getStringLableData(data, labelAttr);
                data.put(labelAttr, stringLableData);
                break;
            }
        }
    }


    /**
     * 返回labelId数组
     * 返回可能为null，获取之后需要进行非null判断
     *
     * @param data 表单数据
     * @param attr 要获取数据的key值
     * @throws XbbException 类型转换失败异常
     * @author chaoxiong.lei
     * @date 14:09 2019/1/23
     * @since v1.0
     **/
    public JSONArray getStringLableData(JSONObject data, String attr) throws XbbException {
        JSONArray result = null;
        if (data == null || attr == null) {
            return result;
        }
        try {
            if (data.get(attr) == null) {
                return null;
            }
            JSONArray labelIds= new JSONArray();
            JSONArray jsonArray = data.getJSONArray(attr);
            try {
                List<LabelPojo> labelPojos = JSON.parseArray(jsonArray.toJSONString(), LabelPojo.class);
                for (LabelPojo item : labelPojos) {
                    labelIds.add(item.getId());
                }
            } catch (Exception e) {
                labelIds = data.getJSONArray(attr);
            }
            return labelIds;
        } catch (Exception e) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100015);
        }
    }
    /**
     * 根据关联数据id判断是否存在关联数据
     *
     * @param corpid
     * @param linkIds
     * @param linkAttr
     * @return
     * @throws XbbException
     */
    public Map<Long, PaasFormDataEntityExt> getEsDataMapByLinkIds(XbbRefTypeEnum xbbRefTypeEnum, String corpid, List<Object> linkIds, String linkAttr) throws XbbException {
        Map<Long, PaasFormDataEntityExt> paasFormDataEntityExtMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
        boolQueryBuilder.filter(termQuery("del", 0));
        boolQueryBuilder.filter(termsQuery(FieldTypeEnum.FLOW_STATUS.getAlias(), Arrays.asList(FlowStatusEnum.NOT_NEED_APPROVAL.getType(), FlowStatusEnum.PASS.getType())));
        //只获取子回款单，如果alone的值是PaymentSheetAloneEnum.NORMAL，则其本身就是自己的明细，无需查询明细
        boolQueryBuilder.filter(termsQuery(FieldTypeEnum.SHEET_ALONE.getAlias(), Arrays.asList(SheetAloneEnum.NORMAL.getCode(), SheetAloneEnum.PARENT.getCode())));
        FormDataListDTO formDataListDTO = new FormDataListDTO();
        formDataListDTO.setCorpid(corpid);
        formDataListDTO.setBusinessType(xbbRefTypeEnum.getCode());
        //处理关联字段条件
        List<ConditionsEntityExt> linkConditions = handleLinkCondition(xbbRefTypeEnum, linkIds, linkAttr);
        //解析查询条件
        EsUtil.parseCondition(boolQueryBuilder, linkConditions, formDataListDTO.getRel(), formDataListDTO.getCorpid(), formDataListDTO.getBusinessType());
        IndexTypeEnum indexTypeEnum =  indexTypeModel.getIndexType(corpid, xbbRefTypeEnum.getCode(), SaasMarkEnum.SAAS.getCode());
        boolQueryBuilder.filter(termQuery(ElasticConstant.MY_JOIN_FIELD,indexTypeEnum.getType()));
        sourceBuilder.query(boolQueryBuilder);
        SearchRequest searchRequest = new SearchRequest(indexTypeEnum.getIndex());
        //查询是否有数据，只需要检测一条就行
        Integer page = linkIds.size() > 0 ? linkIds.size() : 1;
        PageRequest pageRequest = EsUtil.setPage(sourceBuilder, 0, page);
        searchRequest.source(sourceBuilder);
        XbbAggregatedPage<PaasFormDataEntityExt> esEntities = xbbElasticsearchRestTemplate.queryForPages(pageRequest, searchRequest, PaasFormDataEntityExt.class);
        List<PaasFormDataEntityExt> paasFormDataEntityExtList = esEntities.getContent();
        for (PaasFormDataEntityExt paasFormDataEntityExt:paasFormDataEntityExtList ){
            JSONArray linkIdArray = getJsonArrOrDefaultFromFormData(paasFormDataEntityExt.getData(),linkAttr,new JSONArray());
            for (Object linkId: linkIdArray ){
                Long id = Long.valueOf(linkId.toString());
                paasFormDataEntityExtMap.put(id,paasFormDataEntityExt);
            }
        }
        return paasFormDataEntityExtMap;
    }

    /**
     * 格式化redCidPidAmountMap，
     *  得到合同id_应收款id 和 红冲金额对应关系map（如果是进销存，则得到采购合同id_付款计划id 和 红冲金额对应关系map）
     * @param redCidPidAmountMap 合同id_应收款id（采购合同id_付款计划id） 和 红冲金额对应关系map
     * @param entityExt 回/付款单实体
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public void formatCidPidRedAmountMap(String attrContract, String attrPay, Map<String, Double> redCidPidAmountMap, PaasFormDataEntityExt entityExt, double sheetAmount) throws XbbException {
        JSONObject data = entityExt.getData();
        JSONArray idArr = getJsonArrFromFormData(data, attrContract);
        if (idArr == null || idArr.isEmpty()) {
            return;
        }
        //都是一条（因为sheetList内只塞入子、及无子的回/付款单，而这类回/付款单都是只关联一个）
        Long contractId = idArr.getLong(0);
        //回/付款计划信息
        List<Long> pidList = getJsonArrOrDefaultFromFormData(data, attrPay, new JSONArray()).toJavaList(Long.class);
        String key = contractId + "";
        if (pidList.size() > 0) {
            key += "_" + pidList.get(0);
        }
        Double redAmount = redCidPidAmountMap.getOrDefault(key, 0D);
        redAmount += sheetAmount;
        redCidPidAmountMap.put(key, redAmount);
    }

    /**
     * 得到原单剩余可红冲的金额：原单金额 - 原单已经红冲的总金额（注：红冲金额为负）
     * @param sheetAmountDetailGetVO 明细vo
     * @param amountDetailGetPojo 获取金额明细时需要用到的条件、原单信息pojo
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public void subRedAmount(XbbRefTypeEnum xbbRefTypeEnum, SheetAmountDetailGetVO sheetAmountDetailGetVO, AmountDetailGetPojo amountDetailGetPojo) throws XbbException {
        //原单
        PaasFormDataEntityExt linkSheetEntity = amountDetailGetPojo.getLinkSheetEntity();
        //原单金额
        double amount = 0D;
        if (linkSheetEntity != null && linkSheetEntity.getData() != null) {
            JSONObject data = linkSheetEntity.getData();
            amount = getSheetAmountByBusinessType(xbbRefTypeEnum.getCode(), data);
        }
        //原单（linkPreId）关联的红冲单据总额
        double redAmount = amountDetailGetPojo.getRedAmount();
        //剩余总的可红冲金额 = 关联原回款单总金额 - 原单总的已红冲金额(由于红冲金额为负数，因此此处用add)
        Double sumAmount = Arith.add(amount, redAmount);
        sheetAmountDetailGetVO.setSumAmount(sumAmount);
    }

    /**
     * 封装处理最后结果：（1）第一层按照前端传入的(采购)合同id顺序（dataIdList的顺序）；（2）第二层内层按照回/付款计划预计回/付款时间升序-红冲则直接按照原单的明细进行渲染
     * @param paymentSheetFlag true代表回款单，false代表付款单
     * @param sheetAmountDetailGetVO 最终返回结果vo
     * @param dataIdList （采购）合同id集合
     * @param cidPayListMap 合同id-该合同的应收款/回款单list（把同一个合同的应收款/回款单放到一起）。如果是进销存部分则分别对应采购合同、付款计划、付款单，功能一样
     * @param planList 计划list
     * @param cidMap 合同id-合同信息
     * @param cidSignUserIdMap 合同id-签订人userId
     * @param signNameMap 签订人userId-签订人姓名
     * @param attrPlan 未收/实收字段attr
     * @param attrContract 未收/实收字段attr
     * @param attrContract4Plan 回/付款计划关联的合同/采购合同attr
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public void formatDetailResult(boolean paymentSheetFlag, SheetAmountDetailGetVO sheetAmountDetailGetVO, List<Long> dataIdList, Map<Long, List<JSONObject>> cidPayListMap, List<PaasFormDataEntityExt> planList, Map<Long, PaasFormDataEntityExt> cidMap, Map<Long, String> cidSignUserIdMap, Map<String, String> signNameMap, String attrPlan, String attrContract, String attrContract4Plan) {
        Double sumAmount = 0D;
        //拥有付款计划的合同id集合
        List<Long> hasPaymentCids = new ArrayList<>();
        for (PaasFormDataEntityExt entityExt : planList) {
            JSONObject data = entityExt.getData();
            if (data == null) {
                continue;
            }
            Long contractId = data.getLongValue(attrContract4Plan);
            hasPaymentCids.add(contractId);
            PaasFormDataEntityExt contractEntityExt = cidMap.get(contractId);
            String contractSerialNo = contractEntityExt == null ? "" : contractEntityExt.getSerialNo();
            //得到采购合同签订人
            String signUserId = cidSignUserIdMap.getOrDefault(contractId, "");
            String signName = signNameMap.get(signUserId);
            //付款计划信息
            List<IdNamePojo> paymentInfo = Collections.singletonList(new IdNamePojo(entityExt.getDataId(), entityExt.getSerialNo()));
            //已收或未收金额
            Double amount = data.getDoubleValue(attrPlan);
            sumAmount = Arith.add(sumAmount, amount);
            JSONObject jsonObject;
            if (paymentSheetFlag) {
                jsonObject = sheetAmountDetailGetVO.formatInfo(contractId, contractSerialNo, signUserId, signName, paymentInfo, amount, amount);
            } else {
                jsonObject = sheetAmountDetailGetVO.formatInfo4Pay(contractId, contractSerialNo, signUserId, signName, paymentInfo, amount, amount);
            }
            List<JSONObject> list = cidPayListMap.getOrDefault(contractId, new ArrayList<>());
            list.add(jsonObject);
            cidPayListMap.put(contractId, list);
        }
        //最后封装的明细排序规则为：（1）第一层按照前端传入的采购合同id顺序（dataIdList的顺序）；（2）第二层内层按照付款计划预计付款时间升序
        List<JSONObject> list = new ArrayList<>();
        for (Long contractId : dataIdList) {
            if (hasPaymentCids.contains(contractId)) {
                //拥有付款计划的采购合同
                list.addAll(cidPayListMap.get(contractId));
            } else {
                //无付款计划的采购合同
                PaasFormDataEntityExt entityExt = cidMap.get(contractId);
                if (entityExt != null) {
                    String signUserId = cidSignUserIdMap.getOrDefault(contractId, "");
                    String signName = signNameMap.get(signUserId);
                    JSONObject data = entityExt.getData();
                    if (data == null) {
                        continue;
                    }
                    //已收或未收金额
                    Double amount = data.getDoubleValue(attrContract);
                    sumAmount = Arith.add(sumAmount, amount);
                    //应收款信息置为空
                    JSONObject jsonObject;
                    if (paymentSheetFlag) {
                        jsonObject = sheetAmountDetailGetVO.formatInfo(contractId, entityExt.getSerialNo(), signUserId, signName, null, amount, amount);
                    } else {
                        jsonObject = sheetAmountDetailGetVO.formatInfo4Pay(contractId, entityExt.getSerialNo(), signUserId, signName, null, amount, amount);
                    }
                    list.add(jsonObject);
                }
            }
        }
        sheetAmountDetailGetVO.setList(list);
        sheetAmountDetailGetVO.setSumAmount(sumAmount);
    }

    /**
     * 如果是回/付款单，则根据数据data内的回/付款单类型重置sheetTypeEnum（前端传过来的businessType可能没有详细区分哪种类型回/付款单）
     * @param sheetTypeEnum 类型枚举
     * @param data 数据data
     * @return com.xbongbong.paas.enums.SheetTypeEnum
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public SheetTypeEnum getSheetTypeEnumByData(SheetTypeEnum sheetTypeEnum, JSONObject data) {
        if (data == null) {
            return sheetTypeEnum;
        }
        XbbRefTypeEnum xbbRefTypeEnum = sheetTypeEnum.getXbbRefTypeEnum();
        String sheetType = "";
        switch (xbbRefTypeEnum) {
            case PAYMENT_SHEET:
                sheetType = data.getString(PaymentSheetEnum.PAYMENT_SHEET_TYPE.getAttr());
                break;
            case PAY_SHEET:
                sheetType = data.getString(PaySheetEnum.PAY_SHEET_TYPE.getAttr());
                break;
            case OTHER_INCOME:
                sheetType = data.getString(OtherIncomeEnum.SHEET_TYPE_OTHER.getAttr());
                break;
            case OTHER_EXPENSE:
                sheetType = data.getString(OtherExpenseEnum.SHEET_TYPE_OTHER.getAttr());
                break;
            default:
                break;
        }
        if (StringUtil.isNotEmpty(sheetType)) {
            sheetTypeEnum = SheetTypeEnum.getByCodeRefTypeEnum(sheetType, xbbRefTypeEnum);
        }
        return sheetTypeEnum;
    }

    /**
     * 通过流程data内的类型得到回/付款单/其他收入单/其他支出单类型枚举
     * @param businessType 业务类型（流程那边不区分7种类型，需要根据data内的type处理，但是流程data内的type可能是对象格式，需要特别注意）
     * @param dataList 流程data
     * @return com.xbongbong.paas.enums.SheetTypeEnum
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public SheetTypeEnum getSheetTypeEnumByProcssData(Integer businessType, JSONObject dataList) {
        String attrSheetType;
        XbbRefTypeEnum xbbRefTypeEnum;
        if (Objects.equals(businessType, XbbRefTypeEnum.PAYMENT_SHEET.getCode())) {
            attrSheetType = PaymentSheetEnum.PAYMENT_SHEET_TYPE.getAttr();
            xbbRefTypeEnum = XbbRefTypeEnum.PAYMENT_SHEET;
        } else if (Objects.equals(businessType, XbbRefTypeEnum.PAY_SHEET.getCode())) {
            attrSheetType = PaySheetEnum.PAY_SHEET_TYPE.getAttr();
            xbbRefTypeEnum = XbbRefTypeEnum.PAY_SHEET;
        } else if (Objects.equals(businessType, XbbRefTypeEnum.OTHER_INCOME.getCode())) {
            attrSheetType = OtherIncomeEnum.SHEET_TYPE_OTHER.getAttr();
            xbbRefTypeEnum = XbbRefTypeEnum.OTHER_INCOME;
        } else if (Objects.equals(businessType, XbbRefTypeEnum.OTHER_EXPENSE.getCode())) {
            attrSheetType = OtherExpenseEnum.SHEET_TYPE_OTHER.getAttr();
            xbbRefTypeEnum = XbbRefTypeEnum.OTHER_EXPENSE;
        } else {
            //非回/付款单/其他收入单/其他支出单，则直接返回未知
            return SheetTypeEnum.UNKNOWN;
        }
        JSONObject data = JsonHelperUtil.getJSONObject(dataList);
        Object sheetTypeObj = data.get(attrSheetType);
        String sheetType;
        if (sheetTypeObj instanceof JSONObject || sheetTypeObj instanceof HashMap) {
            //此时的type为对象格式
            JSONObject sheetTypeJson = data.getJSONObject(attrSheetType);
            sheetType = sheetTypeJson.getString(BasicConstant.VALUE);
        } else {
            sheetType = sheetTypeObj.toString();
        }
        return SheetTypeEnum.getByCodeRefTypeEnum(sheetType, xbbRefTypeEnum);
    }

    /**
     * 处理数据的回/付款金额和核销金额
     * 已核销、坏账金额：回/付款金额 = 核销金额，正数
     * 预收/付款核销：回/付款金额 = null；只有前端传过来的核销金额（PaymentSheetEnum.WRITE_OFF_AMOUNT.getAttr()）
     * 预收/付款：只有回/付款金额；核销金额为null
     * 红冲已核销、红冲预收/付款核销：回/付款金额 = 核销金额，且都需要置为负数
     *
     * @param writeOff 核销金额，如果为null，则按照默认规则读取（预收/付款核销用核销金额、其他读回/付款金额）；如果不为null，则金额恒用该字段（因为明细内恒用的num_1）
     * @param sheetTypeEnum 回/付款单类型
     * @param sheetAttrPojo 回/付款单的字段attr pojo
     * @param sheetData 回/付款单的data
     * @return com.alibaba.fastjson.JSONObject
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public JSONObject formatAmountWriteOff(Double writeOff, SheetTypeEnum sheetTypeEnum, SheetAttrPojo sheetAttrPojo, JSONObject sheetData) {
        String attrAmount = sheetAttrPojo.getAttrAmount();
        String attrWriteOffAmount = sheetAttrPojo.getAttrWriteOffAmount();
        Double amount = sheetData.getDouble(attrAmount);
        switch (sheetTypeEnum) {
            case WRITE_OFF:
            case BAD_PAYMENT:
            case WRITE_OFF_PAY:
            case BAD_PAY:
                //已核销回/付款、坏账：回/付款金额、核销金额保持一致
                sheetData.put(attrWriteOffAmount, amount);
                break;
            case WRITE_OFF_PREPAYMENT:
            case WRITE_OFF_PREPAY:
                //预收/付款核销：回/付款金额为空，核销金额为正
                Double writeOffAmount;
                //明细内，恒用的num_1；但是非明细的必须按照规则-预收/付款核销读取核销金额
                if (Objects.nonNull(writeOff)) {
                    writeOffAmount = writeOff;
                } else {
                    writeOffAmount = sheetData.getDoubleValue(attrWriteOffAmount);
                }
                sheetData.put(attrWriteOffAmount, writeOffAmount);
                sheetData.remove(attrAmount);
                break;
            case PREPAYMENT:
            case PREPAY:
                //预收/付款：只存回/付款金额，核销金额为空
                break;
            case RED_WRITE_OFF:
            case RED_WRITE_OFF_PREPAYMENT:
            case RED_WRITE_OFF_PAY:
            case RED_WRITE_OFF_PREPAY:
                //红冲已核销、红冲预收/付款核销：回/付款金额，核销金额都为负，且一致
                amount = Arith.mul(amount, -1);
                sheetData.put(attrAmount, amount);
                sheetData.put(attrWriteOffAmount, amount);
                break;
            case RED_WRITE_OFF_BALANCE:
                //红冲预收款核销：回款金额为空，核销金额为负
                Double redWriteOffBalanceAmount;
                //明细内，恒用的num_1；但是非明细的必须按照规则-预收/付款核销读取核销金额
                if (Objects.nonNull(writeOff)) {
                    redWriteOffBalanceAmount = writeOff;
                } else {
                    redWriteOffBalanceAmount = sheetData.getDoubleValue(attrWriteOffAmount);
                }
                redWriteOffBalanceAmount = Arith.mul(redWriteOffBalanceAmount, -1);
                sheetData.put(attrWriteOffAmount, redWriteOffBalanceAmount);
                sheetData.remove(attrAmount);
                break;
            case RED_PREPAYMENT:
            case RED_PREPAY:
            case RED_OTHER_INCOME:
            case RED_OTHER_EXPENSE:
                amount = Arith.mul(amount, -1);
                sheetData.put(attrAmount, amount);
                break;
            default:
                break;
        }
        return sheetData;
    }

    /**
     * 初始化回/付款单、其他收入单、其他支出单实体，设置其基础属性
     * @param userId
     * @param entity
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public void initSheetEntity4Save(String userId, PaasFormDataEntity entity) {
        entity.setId(null);
        if (entity.getOwnerId() == null) {
            entity.setOwnerId(userId);
        }
        if (entity.getDepartmentId() == null) {
            entity.setDepartmentId(0L);
        }
        if (entity.getFlowStatus() == null) {
            entity.setFlowStatus(FlowStatusEnum.NOT_NEED_APPROVAL.getType());
        }
        if (StringUtil.isEmpty(entity.getCreatorId())) {
            entity.setCreatorId(userId);
        }
    }

    /**
     * 根据回/付款单类型设置新建/编辑页的名称以及businessType
     * @param paasFormEntityExt form实体
     * @param sheetTypeEnum 回款单、付款单类型总枚举
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public void setBySheetType(PaasFormEntityExt paasFormEntityExt, SheetTypeEnum sheetTypeEnum){
        Integer businessType = sheetTypeEnum.getBusinessType();
        switch (sheetTypeEnum){
            case WRITE_OFF:
            case PREPAYMENT:
            case WRITE_OFF_PAY:
            case PREPAY:
            case OTHER_INCOME:
            case OTHER_EXPENSE:
                paasFormEntityExt.setBusinessType(businessType);
                break;
            default:
                paasFormEntityExt.setName(sheetTypeEnum.getFormName());
                paasFormEntityExt.setBusinessType(businessType);
                break;
        }
    }

    public void setByReceivablesType(PaasFormEntityExt paasFormEntityExt){
        paasFormEntityExt.setName(XbbRefTypeEnum.RED_RECEIVABLES.getName());
        paasFormEntityExt.setBusinessType(XbbRefTypeEnum.RED_RECEIVABLES.getCode());
    }
    /**
     * 对于没有明细的回/付款单，则把其本身作为明细（方便前端新建or审批时处理）
     * @param data 回/付款单data
     * @param amount 回/付款金额 如果是预收/付款核销，则为核销金额
     * @return com.alibaba.fastjson.JSONArray
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public JSONArray getDefaultAmountDetail(JSONObject data, Double amount){
        JSONArray amountDetail = new JSONArray();

        data.put("amount", amount);
        amountDetail.add(data);
        return amountDetail;
    }

    /**
     * 处理资金账户
     * 如果是0，则拿到所有资金账户id
     *
     * @param accountId 资金账户id
     * @param corpid 公司id
     * @return java.util.List<java.lang.Long>
     * @author 徐俊杰
     * @date 2020/3/18 17:19
     * @since v1.0
     */
    public List<Long> dealWithFundAccount(Long accountId, String corpid) {
        List<Long> ids;
        if (Objects.equals(accountId, FundStatisticsConstant.ALL_ACCOUNT)) {
            //获取该公司所有的资金账户id
            ids = fundAccountModel.getIdsByCorpid(corpid);
            //要把id = 0加入，用以区分是不是全公司
            ids.add(0, FundStatisticsConstant.ALL_ACCOUNT);
        } else {
            //当前账户余额-金额
            ids = Collections.singletonList(accountId);
        }
        return ids;
    }

    /**
     * 处理封装回/付款单关联的合同/采购合同信息
     * @param contractIdIn 回/付款单关联的合同/采购合同id合集
     * @param idAndContractIdMap 回款单id-合同id集合 map
     * @param entityExt 实体
     * @param linkAttr 关联合同/采购合同字段attr
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public void handleSheetIdAndContractIdMap(List<Long> contractIdIn, Map<Long, List<Long>> idAndContractIdMap, PaasFormDataEntityExt entityExt, String linkAttr) throws XbbException {
        List<Long> linkIds = new ArrayList<>();
        JSONObject data = entityExt.getData();
        JSONArray linkIdStr = getJsonArrFromFormData(data, linkAttr);
        List<Long> linkIdList = JsonHelperUtil.parseArray(JSONArray.toJSONString(linkIdStr), Long.class);
        contractIdIn.addAll(linkIdList);
        linkIds.addAll(linkIdList);
        idAndContractIdMap.put(entityExt.getId(), linkIds);
    }

    /**
     * 处理封装回/付款单关联的客户/供应商信息
     * @param linkIdIn 回/付款单关联的 客户/供应商id合集
     * @param idAndContractIdMap 回款单id-客户id集合 map
     * @param entityExt 实体
     * @param linkAttr 关联合同/采购合同字段attr
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public void handleSheetIdAndCusIdMap(List<Long> linkIdIn, Map<Long, List<Long>> idAndContractIdMap, PaasFormDataEntityExt entityExt, String linkAttr) {
        List<Long> linkIds = new ArrayList<>();
        JSONObject data = entityExt.getData();
        //预收/付款、红冲预收/付款余额，需要读取关联客户/关联供应商
        Long linkId = data.getLong(linkAttr);
        linkIdIn.add(linkId);
        linkIds.add(linkId);
        idAndContractIdMap.put(entityExt.getId(), linkIds);
    }

    /**
     * 设置【日期】格式，用回/付款日期/预计回款日期 + 创建时间的时分，如回款日期为2020-5-11，当前时间是2020-5-8 15:16，则入到流水表/对账单表的时间为 2020-5-11 15:16
     * 设置【日期时间】格式，直接使用用户选择的回/付款时间/预计回款日期
     * @param data 回/付款数据data，或应收款数据data
     * @param attrTime 回/付款时间字段attr，或应收款的预计收款时间attr
     * @param explainMap 字段解释map
     * @param addTime 创建时间
     * @return java.lang.Long
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public Long getSheetTime4FundFlow(JSONObject data, String attrTime, Map<String, FieldAttrEntity> explainMap, Long addTime){
        FieldAttrEntity timeAttrEntity = null;
        if (Objects.nonNull(explainMap)) {
            timeAttrEntity = explainMap.get(attrTime);
        }
        return getSheetTime4FlowAndStatement(data, attrTime, timeAttrEntity, addTime);
    }
    /**
     * 设置【日期】格式，用回/付款日期/预计回款日期 + 创建时间的时分，如回款日期为2020-5-11，当前时间是2020-5-8 15:16，则入到流水表/对账单表的时间为 2020-5-11 15:16
     * 设置【日期时间】格式，直接使用用户选择的回/付款时间/预计回款日期
     * @param data 回/付款数据data，或应收款数据data
     * @param attrTime 回/付款时间字段attr，或应收款的预计收款时间attr
     * @param timeAttrEntity 时间字段
     * @param addTime 创建时间
     * @return java.lang.Long
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public Long getSheetTime4FlowAndStatement(JSONObject data, String attrTime, FieldAttrEntity timeAttrEntity, Long addTime){
        if (Objects.isNull(data)) {
            return BasicConstant.ZERO_LONG;
        }
        Long sheetTime = data.getLongValue(attrTime);
        if (Objects.equals(sheetTime, BasicConstant.ZERO_LONG)) {
            return sheetTime;
        }
        //默认当做日期格式，后面再去拼当前时间的时分
        String dateType = DateTimeEnum.TIME.getType();
        if (Objects.nonNull(timeAttrEntity)) {
            //如果设置过字段解释，则使用字段解释内的配置
            dateType = timeAttrEntity.getDateType();
        }
        if (Objects.equals(dateType, DateTimeEnum.TIME.getType())) {
            //用户设置的为【日期】格式，则用如此规则进行拼接流水内的时间：回/付款日期/预计回款日期 + addTime内的时分
            //sheetTime的日期，不用DateTimeUtil.SDFDate，以防线程调用时出错
            String date = DateTimeUtil.getStringEpochSecond(sheetTime, "yyyy-MM-dd");
            //addTime的时分
            if (Objects.isNull(addTime)) {
                addTime = DateTimeUtil.getInt();
            }
            String time = DateTimeUtil.getHourMinSecond(addTime);
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append(date).append(SymbolConstant.SPACE).append(time);
            try {
                sheetTime = DateTimeUtil.getInt(stringBuilder.toString(), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
            }catch (Exception e){
                LOG.warn("getSheetTime4FundFlow：" + e);
                LOG.warn("stringBuilder：" + stringBuilder.toString() +"  sheetTime:"+ sheetTime+"  date-yyyy-MM-dd:"+ date+ "  addTimeMM：" + time);
            }
            return sheetTime;
        }
        //非日期格式，则直接使用用户设置的回/付款时间
        return sheetTime;
    }

    /**
     * 校验时间参数，如果合法则把startTime、endTime塞入StatementCustomerPojo
     * customerId、和customerIdList都不需要
     * @param checkingDate 对账日期范围筛选：[1601481600, 1603555199]
     * @return com.xbongbong.pro.customerstatement.pojo.StatementCustomerPojo
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public StatementCustomerPojo getStatementCustomerPojo(List<Long> checkingDate) throws XbbException {
        return getStatementCustomerPojo(checkingDate, null, null);
    }
    /**
     * 校验时间参数，如果合法则把startTime、endTime塞入StatementCustomerPojo，并同时把客户id信息塞入
     * @param checkingDate 对账日期范围筛选：[1601481600, 1603555199]
     * @param customerId 客户id，如不需要则置空，比如获取对账单外层客户id列表接口、汇总计算接口无需该参数
     * @param customerIdList 客户id集合，如不需要则置空，比如获取对账单外层客户id列表接口、获取某个客户对账单接口无需该参数
     * @return com.xbongbong.pro.customerstatement.pojo.StatementCustomerPojo
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public StatementCustomerPojo getStatementCustomerPojo(List<Long> checkingDate, Long customerId, List<Long> customerIdList) throws XbbException {
        //对账日期筛选
        if (CollectionUtils.isEmpty(checkingDate)) {
            throw new XbbException(StatementErrorCodeEnum.API_ERROR_249001);
        }
        if (checkingDate.size()< BasicConstant.TWO) {
            throw new XbbException(StatementErrorCodeEnum.API_ERROR_249002);
        }
        //开始结束日期
        Long startTime = checkingDate.get(0);
        Long endTime = checkingDate.get(1);
        Long sub = endTime - startTime;
        if (startTime > endTime) {
            throw new XbbException(StatisticsServicesErrorCodeEnum.API_ERROR_224007, StatisticsServicesErrorCodeEnum.API_ERROR_224007.getMsg(),"，"+ I18nMessageUtil.getMessage(I18nStringConstant.END_TIME_REGULATIONS));
        }
        if (sub > TimeConstant.SECONDS_SIX_MONTH) {
            throw new XbbException(StatementErrorCodeEnum.API_ERROR_249003);
        }
        return new StatementCustomerPojo(startTime, endTime, customerId, customerIdList);
    }

    /**
     * 得到对账日期范围内的客户id集合（还未限制客户负责人、协同人等）
     * @param conditions 筛选条件，客户id搜索
     * @param corpid 公司id
     * @param statementCustomerPojo
     * @return java.util.Set<java.lang.Long>
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public Set<Long> getStatementCustomerIdList(List<ConditionsEntityExt> conditions, String corpid, StatementCustomerPojo statementCustomerPojo) throws XbbException {
        //corpid、del、checkingDate(开始、结束)
        BoolQueryBuilder boolQueryBuilder = chartHelp.statementQuery4CheckData(corpid, statementCustomerPojo);
        //解析查询条件：客户id搜索
        EsUtil.parseCondition(boolQueryBuilder, conditions, null, corpid, XbbRefTypeEnum.CUSTOMER_STATEMENT.getCode());
        List<String> fieldList = Collections.singletonList("data." + CustomerStatementEnum.CUSTOMER_ID.getAttr());
        List<CustomerStatementEntity> statementList = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_CUSTOMER_STATEMENT, boolQueryBuilder, CustomerStatementEntity.class, fieldList);
        return statementList.stream().map(item -> item.getData().getLong(CustomerStatementEnum.CUSTOMER_ID.getAttr())).collect(Collectors.toSet());
    }
    /**
     * 得到所有期初金额
     * @param corpid
     * @param statementCustomerPojo
     * @return double
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public double getStatementBegining(String corpid, StatementCustomerPojo statementCustomerPojo) throws XbbException {
        /*
         期初 = (截止至"对账日期"的开始日期所有)应收总金额  - 核销总金额
         */
        BoolQueryBuilder beginingQueryBuilder = chartHelp.statementQuery4Begining(corpid, statementCustomerPojo);
        return getStatementTotalBalanceReceivable(beginingQueryBuilder, IndexTypeEnum.IDX_SAAS_CUSTOMER_STATEMENT);
    }
    /**
     * 得到符合条件的应收总金额
     * @param boolQueryBuilder
     * @return double
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public double getStatementTotalBalanceReceivable(BoolQueryBuilder boolQueryBuilder, IndexTypeEnum indexTypeEnum) throws XbbException {
        /*
         汇总两个字段的值：应收金额、核销金额
         */
        String amountReceivableAttr = CustomerStatementEnum.getEsAttr(CustomerStatementEnum.AMOUNT_RECEIVABLE);
        String writeOffAmountAttr = CustomerStatementEnum.getEsAttr(CustomerStatementEnum.WRITE_OFF_AMOUNT);
        String balanceReceivableAttr = CustomerStatementEnum.getEsAttr(CustomerStatementEnum.BALANCE_RECEIVABLE);
        List<String> sumFieldList = Arrays.asList(amountReceivableAttr, writeOffAmountAttr, balanceReceivableAttr);
        //一次性汇总应收金额、核销金额
        JSONObject result = esHelper.sumMuchField4Some(indexTypeEnum, boolQueryBuilder, sumFieldList);
        double totalAmountReceivable = result.getDoubleValue(amountReceivableAttr);
        double totalWriteOffAmount = result.getDoubleValue(writeOffAmountAttr);
        double balanceReceivable = result.getDoubleValue(balanceReceivableAttr);
        /*
         应收总余额 = (符合条件的)应收总金额  - 核销总金额 + 期初（对账详细中使用）
         */
        double totalBalanceReceivable = Arith.sub(Arith.add(totalAmountReceivable, balanceReceivable), totalWriteOffAmount);
        return totalBalanceReceivable;
    }

    /**
     * 格式化金额
     * @param money 金额
     * @return java.lang.String
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public String formatMoneyStr(double money) {
        return Objects.equals(money, BasicConstant.ZERO_DOUBLE) ? "0" : StringUtil.formatDouble(money);
    }
    /**
     * 格式化金额
     * @param money 金额
     * @param defaultStr money如果为null，则返回该值
     * @return java.lang.String
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public String formatMoneyStr(Double money, String defaultStr) {
        if (Objects.isNull(money)) {
            return defaultStr;
        }
        return formatMoneyStr(money);
    }

    /**
     * 经销商回款单/销项发票审批默认流程的FieldPermissionEntityExtList
     * @return java.util.List<com.xbongbong.paas.domain.entity.ext.FieldPermissionEntityExt>
     * @param businessType 业务类型
     * @param showTypeList 要显示到流程设计也的字段showType类型
     * @return java.util.Map<java.lang.Integer,java.util.List<com.xbongbong.paas.domain.entity.ext.FieldPermissionEntityExt>> key为业务类型
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public Map<Integer, List<FieldPermissionEntityExt>> getDefaultFieldPermission4Distributor(Integer businessType, List<Integer> showTypeList){
        List<FieldPermissionEntityExt> list = new ArrayList<>();
        if (Objects.equals(businessType, XbbRefTypeEnum.PAYMENT_SHEET.getCode())) {
            List<PaymentSheetDistributorEnum> sheetList = Arrays.asList(PaymentSheetDistributorEnum.values());
            List<String> sheetNoneEditMustShowAttrList = PaymentSheetBaseEnum.processNoneEditMustShow(DistributorMarkEnum.DISTRIBUTOR.getCode());
            for (PaymentSheetDistributorEnum distributorEnum : sheetList) {
                if (commonHelp.processNotShow(distributorEnum.getEditHide(), distributorEnum.getShowType(), showTypeList)) {
                    continue;
                }
                FieldPermissionEntityExt fieldPermissionEntityExt = new FieldPermissionEntityExt();
                BeanUtil.copyProperties(distributorEnum, fieldPermissionEntityExt, true);
                String attr = distributorEnum.getAttr();
                if (Objects.equals(attr, PaymentSheetDistributorEnum.SHEET_NO.getAttr())) {
                    fieldPermissionEntityExt.setSummary(BasicConstant.ONE);
                } else {
                    fieldPermissionEntityExt.setSummary(BasicConstant.ZERO);
                }
                if (sheetNoneEditMustShowAttrList.contains(attr)) {
                    fieldPermissionEntityExt.setEditable(BasicConstant.ZERO);
                } else {
                    fieldPermissionEntityExt.setEditable(BasicConstant.ONE);
                }
                list.add(fieldPermissionEntityExt);
            }
        } else if (Objects.equals(businessType, XbbRefTypeEnum.INVOICE.getCode())) {
            List<InvoiceDistributorEnum> invoiceList = Arrays.asList(InvoiceDistributorEnum.values());
            List<Object> summaryAttrList = InvoiceDistributorEnum.getSummaryAttrList();
            for (InvoiceDistributorEnum distributorEnum : invoiceList) {
                if (commonHelp.processNotShow(distributorEnum.getEditHide(), distributorEnum.getShowType(), showTypeList)) {
                    continue;
                }
                FieldPermissionEntityExt fieldPermissionEntityExt = new FieldPermissionEntityExt();
                BeanUtil.copyProperties(distributorEnum, fieldPermissionEntityExt, true);
                if (summaryAttrList.contains(distributorEnum)) {
                    fieldPermissionEntityExt.setSummary(BasicConstant.ONE);
                } else {
                    fieldPermissionEntityExt.setSummary(BasicConstant.ZERO);
                }
                fieldPermissionEntityExt.setEditable(BasicConstant.ZERO);
                list.add(fieldPermissionEntityExt);
            }
        } else if (Objects.equals(businessType, XbbRefTypeEnum.CREDIT_TEMPORARY_LIMIT.getCode())) {
            List<CreditTemporaryLimitEnum> temporaryLimitEnums = Arrays.asList(CreditTemporaryLimitEnum.values());
            List<String> editableAttrList = CreditTemporaryLimitEnum.processNoneEditMustShow(DistributorMarkEnum.DISTRIBUTOR.getCode());
            List<Object> summaryAttrList = CreditTemporaryLimitEnum.getSummaryAttrList();
            for (CreditTemporaryLimitEnum temporaryLimitEnum : temporaryLimitEnums) {
                if (commonHelp.processNotShow(temporaryLimitEnum.getEditHide(), temporaryLimitEnum.getShowType(), showTypeList)) {
                    continue;
                }
                FieldPermissionEntityExt fieldPermissionEntityExt = new FieldPermissionEntityExt();
                BeanUtil.copyProperties(temporaryLimitEnum, fieldPermissionEntityExt, true);
                if (summaryAttrList.contains(temporaryLimitEnum)) {
                    fieldPermissionEntityExt.setSummary(BasicConstant.ONE);
                } else {
                    fieldPermissionEntityExt.setSummary(BasicConstant.ZERO);
                }
                if (editableAttrList.contains(temporaryLimitEnum.getAttr())) {
                    fieldPermissionEntityExt.setEditable(BasicConstant.ZERO);
                } else {
                    fieldPermissionEntityExt.setEditable(BasicConstant.ONE);
                }
                list.add(fieldPermissionEntityExt);
            }
        } else if (Objects.equals(businessType, XbbRefTypeEnum.CREDIT_FIXED_LIMIT.getCode())) {
            List<CreditFixedLimitEnum> fixedLimitEnums = Arrays.asList(CreditFixedLimitEnum.values());
            List<String> editableAttrList = CreditFixedLimitEnum.processNoneEditMustShow(DistributorMarkEnum.DISTRIBUTOR.getCode());
            List<Object> summaryAttrList = CreditFixedLimitEnum.getSummaryAttrList();
            for (CreditFixedLimitEnum fixedLimitEnum : fixedLimitEnums) {
                if (commonHelp.processNotShow(fixedLimitEnum.getEditHide(), fixedLimitEnum.getShowType(), showTypeList)) {
                    continue;
                }
                FieldPermissionEntityExt fieldPermissionEntityExt = new FieldPermissionEntityExt();
                BeanUtil.copyProperties(fixedLimitEnum, fieldPermissionEntityExt, true);
                if (summaryAttrList.contains(fixedLimitEnum)) {
                    fieldPermissionEntityExt.setSummary(BasicConstant.ONE);
                } else {
                    fieldPermissionEntityExt.setSummary(BasicConstant.ZERO);
                }
                if (editableAttrList.contains(fixedLimitEnum.getAttr())) {
                    fieldPermissionEntityExt.setEditable(BasicConstant.ZERO);
                } else {
                    fieldPermissionEntityExt.setEditable(BasicConstant.ONE);
                }
                list.add(fieldPermissionEntityExt);
            }
        } else if (Objects.equals(businessType, XbbRefTypeEnum.REFUND.getCode())) {
            List<Object> summaryAttrList = ReturnEnum.getSummaryAttrList();
            for (ReturnEnum returnEnum : ReturnEnum.values()) {
                if (commonHelp.processNotShow(returnEnum.getEditHide(), returnEnum.getShowType(), showTypeList)) {
                    continue;
                }
                FieldPermissionEntityExt fieldPermissionEntityExt = new FieldPermissionEntityExt();
                BeanUtil.copyProperties(returnEnum, fieldPermissionEntityExt, true);
                if (summaryAttrList.contains(returnEnum)) {
                    fieldPermissionEntityExt.setSummary(BasicConstant.ONE);
                } else {
                    fieldPermissionEntityExt.setSummary(BasicConstant.ZERO);
                }
                fieldPermissionEntityExt.setEditable(BasicConstant.ZERO);
                list.add(fieldPermissionEntityExt);
            }
        }
        Map<Integer, List<FieldPermissionEntityExt>> map = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        map.put(businessType, list);
        return map;
    }

    /**
     * 得到经销商初始化的默认审批流程内的节点默认条件
     * @param businessType
     * @return java.util.Map<java.lang.String,java.util.List<com.xbongbong.paas.domain.entity.ext.ConditionsEntityExt>>
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public Map<String, List<ConditionsEntityExt>> getDefaultConditions4Distributor(Integer businessType){
        /*
         key：优先级，DefaultDistributorProcessEnum 内的uuid
         value：条件，数组格式
         */
        Map<String, List<ConditionsEntityExt>> map = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        if (Objects.equals(businessType, XbbRefTypeEnum.PAYMENT_SHEET.getCode())) {
            /*
              优先级1：支付方式-线下打款，代客操作
              优先级2：回款单类型-所有红冲类型-红冲已核销，红冲预收款核销，红冲预收款核销(退到余额)，红冲预收款余额
             */
            PaymentSheetDistributorEnum payMethod = PaymentSheetDistributorEnum.PAY_METHOD;
            PaymentSheetDistributorEnum paymentSheetType = PaymentSheetDistributorEnum.PAYMENT_SHEET_TYPE;
            //支付方式：线下打款，代客支付 进审批
            List<ConditionsEntityExt> conditionOne = new ArrayList<>();
            conditionOne.add(new ConditionsEntityExt(payMethod.getAttr(), payMethod.getAttrName(), ConditionEnum.IN.getSymbol(), PayMethodDistributorEnum.mustIntoProcess(), payMethod.getFieldType()));
            //回款单类型：4种红冲类型进审批
            List<ConditionsEntityExt> conditionTwo = new ArrayList<>();
            conditionTwo.add(new ConditionsEntityExt(paymentSheetType.getAttr(), paymentSheetType.getAttrName(), ConditionEnum.IN.getSymbol(), PaymentSheetTypeEnum.mustIntoProcess(), paymentSheetType.getFieldType()));
            map.put(DefaultDistributorProcessEnum.PAYMENT_SHEET_3.getUid(), conditionOne);
            map.put(DefaultDistributorProcessEnum.PAYMENT_SHEET_4.getUid(), conditionTwo);
        }
        return map;
    }

    /**
     * 从预收款余额内移入，封装关联业务的参数
     * @param businessType
     * @param linkId
     * @param linkName
     * @param distributorMark
     * @return java.util.List<com.xbongbong.pro.formdata.pojo.LinkBusinessSinglePojo>
     * @since v1.0
     * @version v1.0
     */
    public List<LinkBusinessSinglePojo> getLinkBusinessSinglePojos(Integer businessType, Long linkId, String linkName, Integer distributorMark) {
        LinkBusinessSinglePojo linkBusinessSinglePojo = new LinkBusinessSinglePojo();
        linkBusinessSinglePojo.setBusinessType(businessType);
        linkBusinessSinglePojo.setId(linkId);
        linkBusinessSinglePojo.setName(linkName);
        linkBusinessSinglePojo.setSaasMark(SaasMarkEnum.SAAS.getCode());
        linkBusinessSinglePojo.setDistributorMark(distributorMark);
        List<LinkBusinessSinglePojo> linkBusinessSinglePojos = new ArrayList<>(1);
        linkBusinessSinglePojos.add(linkBusinessSinglePojo);
        return linkBusinessSinglePojos;
    }

    //--------------------以下是私有private方法
    /**
     * 处理回款单/付款单的关联字段条件,主要用户合同/客户/付款计划/应收款的删除判断
     * @param xbbRefTypeEnum 业务类型
     * @param linkIds 关联ids
     * @param linkAttr 关联字段
     * @return java.util.List<com.xbongbong.paas.domain.entity.ext.ConditionsEntityExt>
     * @author chenshan
     * @since v1.0
     * @version v1.0
     */
    private List<ConditionsEntityExt> handleLinkCondition(XbbRefTypeEnum xbbRefTypeEnum, List<Object> linkIds, String linkAttr){
        List<ConditionsEntityExt> linkConditions = new ArrayList<>();
        if (StringUtil.isEmpty(linkAttr)) {
            return linkConditions;
        }
        ConditionsEntityExt conditionsEntityExt = new ConditionsEntityExt();
        switch (xbbRefTypeEnum) {
            case PAYMENT_SHEET:
                if (Objects.equals(PaymentSheetEnum.PAYMENT.getAttr(), linkAttr) || Objects.equals(PaymentSheetEnum.CONTRACT.getAttr(), linkAttr)) {
                    conditionsEntityExt.setSymbol(ConditionEnum.INCLUDE.getSymbol());
                }
                break;
            case PAY_SHEET:
                if ( Objects.equals(PaySheetEnum.PAY_PLAN.getAttr(), linkAttr) || Objects.equals(PaySheetEnum.LINK_PURCHASE.getAttr(), linkAttr)) {
                    conditionsEntityExt.setSymbol(ConditionEnum.INCLUDE.getSymbol());
                }
                break;
            default:
                break;
        }
        conditionsEntityExt.setAttr(linkAttr);
        conditionsEntityExt.setValue(linkIds);
        conditionsEntityExt.setFieldType(FieldTypeEnum.TEXT.getType());
        linkConditions.add(conditionsEntityExt);
        return linkConditions;
    }

    /**
     * 处理回款单/付款单的关联字段条件
     * @param xbbRefTypeEnum 业务类型
     * @param linkId 关联id
     * @param linkAttr 关联字段
     * @return java.util.List<com.xbongbong.paas.domain.entity.ext.ConditionsEntityExt>
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private List<ConditionsEntityExt> handleLinkCondition(XbbRefTypeEnum xbbRefTypeEnum, Long linkId, String linkAttr){
        List<ConditionsEntityExt> linkConditions = new ArrayList<>();
        if (StringUtil.isEmpty(linkAttr)) {
            return linkConditions;
        }
        ConditionsEntityExt conditionsEntityExt = new ConditionsEntityExt();
        switch (xbbRefTypeEnum) {
            case PAYMENT_SHEET:
                //关联客户 与 关联合同/应收款 的symbol不一致
                if (Objects.equals(PaymentSheetEnum.CUSTOMER_ID.getAttr(), linkAttr)) {
                    //必须
                    conditionsEntityExt.setSymbol(ConditionEnum.EQUAL.getSymbol());
                } else {
                    //包含
                    conditionsEntityExt.setSymbol(ConditionEnum.LIKE.getSymbol());
                }
                break;
            case PAY_SHEET:
                //关联供应商 与 关联采购合同/付款计划 的symbol不一致
                if (Objects.equals(PaySheetEnum.LINK_SUPPLIER.getAttr(), linkAttr)) {
                    //必须
                    conditionsEntityExt.setSymbol(ConditionEnum.EQUAL.getSymbol());
                } else {
                    //包含
                    conditionsEntityExt.setSymbol(ConditionEnum.LIKE.getSymbol());
                }
                break;
            default:
                break;
        }
        conditionsEntityExt.setAttr(linkAttr);
        conditionsEntityExt.setValue(Collections.singletonList(linkId));
        conditionsEntityExt.setFieldType(FieldTypeEnum.TEXT.getType());
        linkConditions.add(conditionsEntityExt);
        return linkConditions;
    }

    /**
     * 读取回款单/付款单、其他收入单、其他支出单的查询条件，主要用于处理关联新建的查询接口
     * @param conditionsEntityExtList
     * @param isRed
     * @param linkBusinessType
     * @return
     */
    private List<ConditionsEntityExt> handLinkTypeConditions(List<ConditionsEntityExt> conditionsEntityExtList, Integer isRed,XbbRefTypeEnum linkBusinessType){
        switch (linkBusinessType) {
            case PAYMENT_SHEET:
                if (Objects.equals(isRed, BasicConstant.ONE)) {
                    //只读取所有红冲类型的回款单
                    List<Object> redCodeList = new ArrayList<>(PaymentSheetTypeEnum.getRedCodeList());
                    ConditionsEntityExt redConditionsEntityExt = new ConditionsEntityExt(PaymentSheetEnum.PAYMENT_SHEET_TYPE.getAttr(), "", PaymentSheetEnum.PAYMENT_SHEET_TYPE.getFieldType(), ConditionEnum.IN.getSymbol(), redCodeList);
                    conditionsEntityExtList.add(redConditionsEntityExt);
                }
                break;
            case PAY_SHEET:
                if (Objects.equals(isRed, BasicConstant.ONE)) {
                    //只读取所有红冲类型的回款单
                    List<Object> redCodeList = new ArrayList<>(PaySheetTypeEnum.getRedCodeList());
                    ConditionsEntityExt redConditionsEntityExt = new ConditionsEntityExt(PaySheetEnum.PAY_SHEET_TYPE.getAttr(), "", PaySheetEnum.PAY_SHEET_TYPE.getFieldType(), ConditionEnum.IN.getSymbol(), redCodeList);
                    conditionsEntityExtList.add(redConditionsEntityExt);
                }
                break;
            case OTHER_INCOME:
                if (Objects.equals(isRed, BasicConstant.ONE)) {
                    //只读取所有红冲类型的其他收入单
                    List<Object> redCodeList = new ArrayList<>(OtherIncomeSheetTypeEnum.getRedCodeList());
                    ConditionsEntityExt redConditionsEntityExt = new ConditionsEntityExt(OtherIncomeEnum.SHEET_TYPE_OTHER.getAttr(), "", OtherIncomeEnum.SHEET_TYPE_OTHER.getFieldType(), ConditionEnum.IN.getSymbol(), redCodeList);
                    conditionsEntityExtList.add(redConditionsEntityExt);
                }
                //其他收入单不用塞入alone条件
                return conditionsEntityExtList;
            case OTHER_EXPENSE:
                if (Objects.equals(isRed, BasicConstant.ONE)) {
                    //只读取所有红冲类型的其他支出单
                    List<Object> redCodeList = new ArrayList<>(OtherExpenseSheetTypeEnum.getRedCodeList());
                    ConditionsEntityExt redConditionsEntityExt = new ConditionsEntityExt(OtherExpenseEnum.SHEET_TYPE_OTHER.getAttr(), "", OtherExpenseEnum.SHEET_TYPE_OTHER.getFieldType(), ConditionEnum.IN.getSymbol(), redCodeList);
                    conditionsEntityExtList.add(redConditionsEntityExt);
                }
                //其他支出单不用塞入alone条件
                return conditionsEntityExtList;
            default:
                return conditionsEntityExtList;
        }
        ConditionsEntityExt aloneConditionsEntityExt = new ConditionsEntityExt();
        aloneConditionsEntityExt.setAttr(FieldTypeEnum.SHEET_ALONE.getAlias());
        aloneConditionsEntityExt.setFieldType(FieldTypeEnum.SHEET_ALONE.getType());
        aloneConditionsEntityExt.setSymbol(ConditionEnum.IN.getSymbol());
        aloneConditionsEntityExt.setValue(Arrays.asList(SheetAloneEnum.NORMAL.getCode(), SheetAloneEnum.PARENT.getCode()));
        conditionsEntityExtList.add(aloneConditionsEntityExt);
        return conditionsEntityExtList;
    }

    /**
     * 处理各种类型回/付款单的字段
     * @param unableEditAttrList 不能编辑的字段
     * @param sheetTypeEnum 回/付款单类型枚举
     * @param explainList 字段解释list
     * @param distributorMark 经销商类型标识
     * @return java.util.List<FieldAttrEntity>
     * @author zcp
     * @date 19/11/13 013 22:14
     * @update 19/11/13 013 22:14
     * @since v1.0
     * @version v1.0
     */
    private List<FieldAttrEntity> handlePaymentSheet(List<String> unableEditAttrList, SheetTypeEnum sheetTypeEnum, List<FieldAttrEntity> explainList, Integer distributorMark) {
        XbbRefTypeEnum xbbRefTypeEnum = sheetTypeEnum.getXbbRefTypeEnum();
        //此处因为是显示，所以要展示所有的回/付款单类型
        List<ItemPoJo> itemList;
        String sheetTypeAttr;
        switch (xbbRefTypeEnum) {
            case PAY_SHEET:
                itemList = dataDictionaryModel.getDictionaryList4PaySheetType(true);
                sheetTypeAttr = PaySheetEnum.PAY_SHEET_TYPE.getAttr();
                break;
            case OTHER_INCOME:
                itemList = dataDictionaryModel.getDictionaryList4OtherIncomeSheetType();
                sheetTypeAttr = OtherIncomeEnum.SHEET_TYPE_OTHER.getAttr();
                break;
            case OTHER_EXPENSE:
                itemList = dataDictionaryModel.getDictionaryList4OtherExpenseSheetType();
                sheetTypeAttr = OtherExpenseEnum.SHEET_TYPE_OTHER.getAttr();
                break;
            default:
                itemList = dataDictionaryModel.getDictionaryList4PaymentSheetType(true);
                sheetTypeAttr = PaymentSheetEnum.PAYMENT_SHEET_TYPE.getAttr();
                break;
        }
        String memo = sheetTypeEnum.getMemo();
        switch (sheetTypeEnum) {
            case WRITE_OFF:
            case WRITE_OFF_PAY:
            case OTHER_INCOME:
            case OTHER_EXPENSE:
                boolean distributorMarkFlag = Objects.equals(distributorMark, DistributorMarkEnum.DISTRIBUTOR.getCode());
                //厂商端新建经销商类型已核销回款单，支付方式默认为代客操作
                String methodValue = PayMethodDistributorEnum.PRO_OPERATE.getCode();
                //编辑时，部分字段需要禁止修改
                for (FieldAttrEntity fieldAttrEntity : explainList) {
                    handleAttr4Editable(unableEditAttrList, fieldAttrEntity, fieldAttrEntity.getAttr(), sheetTypeAttr, memo, itemList);
                    if (distributorMarkFlag && Objects.equals(fieldAttrEntity.getAttr(), PaymentSheetDistributorEnum.PAY_METHOD.getAttr())) {
                        commonHelp.handleLinkAdd4Combo(fieldAttrEntity, methodValue);
                    }
                }
                break;
            case WRITE_OFF_PREPAYMENT:
            case WRITE_OFF_PREPAY:
                //预收/付款核销要移除的字段，除了要移除部分系统字段，还需要【移除自定义字段】
                List<String> removeAttrList = SheetAssembleEnum.WriteOffPrepayment.removeAttr(xbbRefTypeEnum);
                //attr-要更新的字段枚举
                Map<String, SheetAssembleEnum.WriteOffPrepayment> updateMap = SheetAssembleEnum.WriteOffPrepayment.updateAttr(xbbRefTypeEnum);
                Iterator<FieldAttrEntity> iterator = explainList.iterator();
                while (iterator.hasNext()) {
                    FieldAttrEntity fieldAttrEntity = iterator.next();
                    handleAttr4WriteOffPrepayment(xbbRefTypeEnum, unableEditAttrList, removeAttrList, updateMap, iterator, fieldAttrEntity, memo, itemList);
                }
                break;
            case PREPAYMENT:
            case PREPAY:
                //预收/付款要移除的字段
                removeAttrList = SheetAssembleEnum.Prepayment.removeAttr(xbbRefTypeEnum);
                iterator = explainList.iterator();
                while (iterator.hasNext()) {
                    FieldAttrEntity fieldAttrEntity = iterator.next();
                    //预收/付款：移除三个系统字段
                    handleAttr4Prepayment(xbbRefTypeEnum, unableEditAttrList, removeAttrList, iterator, fieldAttrEntity, memo, itemList);
                }
                break;
            case RED_WRITE_OFF:
            case RED_WRITE_OFF_PREPAYMENT:
            case RED_WRITE_OFF_PAY:
            case RED_WRITE_OFF_PREPAY:
                //红冲已核销、红冲预收/付款核销、红冲预收款核销(退到余额)要移除的字段
                removeAttrList = SheetAssembleEnum.RedWriteOff.removeAttr(xbbRefTypeEnum);
                //attr-要更新的字段枚举
                Map<String, SheetAssembleEnum.RedWriteOff> redUpdateMap = SheetAssembleEnum.RedWriteOff.updateAttr(xbbRefTypeEnum);
                iterator = explainList.iterator();
                while (iterator.hasNext()) {
                    FieldAttrEntity fieldAttrEntity = iterator.next();
                    handleAttr4RedWriteOff(xbbRefTypeEnum, unableEditAttrList, removeAttrList, iterator, redUpdateMap, fieldAttrEntity, memo, itemList);
                }
                break;
            case RED_WRITE_OFF_BALANCE:
                //红冲预收款核销(退到余额)要移除的字段
                removeAttrList = SheetAssembleEnum.RedWriteOffBalance.removeAttr(xbbRefTypeEnum);
                //attr-要更新的字段枚举
                Map<String, SheetAssembleEnum.RedWriteOffBalance> redWriteOffBalanceMap = SheetAssembleEnum.RedWriteOffBalance.updateAttr(xbbRefTypeEnum);
                iterator = explainList.iterator();
                while (iterator.hasNext()) {
                    FieldAttrEntity fieldAttrEntity = iterator.next();
                    handleAttr4RedWriteOffBalance(xbbRefTypeEnum, unableEditAttrList, removeAttrList, redWriteOffBalanceMap, iterator, fieldAttrEntity, memo, itemList);
                }
                break;
            case RED_PREPAYMENT:
            case RED_PREPAY:
                //红冲预收/付款要移除的字段
                removeAttrList = SheetAssembleEnum.RedPrepayment.removeAttr(xbbRefTypeEnum);
                //attr-要更新的字段枚举
                Map<String, SheetAssembleEnum.RedPrepayment> redPrepaymentUpdateMap = SheetAssembleEnum.RedPrepayment.updateAttr(xbbRefTypeEnum);
                iterator = explainList.iterator();
                while (iterator.hasNext()) {
                    FieldAttrEntity fieldAttrEntity = iterator.next();
                    handleAttr4RedPrepayment(xbbRefTypeEnum, unableEditAttrList, removeAttrList, iterator, redPrepaymentUpdateMap, fieldAttrEntity, memo, itemList);
                }
                break;
            case BAD_PAYMENT:
            case BAD_PAY:
                //坏账要移除的字段
                removeAttrList = SheetAssembleEnum.BadPayment.removeAttr(xbbRefTypeEnum);
                //attr-要更新的字段枚举
                Map<String, SheetAssembleEnum.BadPayment> badUpdateMap = SheetAssembleEnum.BadPayment.updateAttr(xbbRefTypeEnum);
                iterator = explainList.iterator();
                while (iterator.hasNext()) {
                    FieldAttrEntity fieldAttrEntity = iterator.next();
                    handleAttr4BadPayment(xbbRefTypeEnum, unableEditAttrList, removeAttrList, iterator, badUpdateMap, fieldAttrEntity, memo, itemList);
                }
                break;
            case RED_OTHER_INCOME:
            case RED_OTHER_EXPENSE:
                //红冲其他收入单/其他支出单，即红冲已收款/红冲已付款 没有需要移除的字段
                //attr-要更新的字段枚举
                Map<String, SheetAssembleEnum.RedOther> redOtherUpdateMap = SheetAssembleEnum.RedOther.updateAttr(xbbRefTypeEnum);
                iterator = explainList.iterator();
                while (iterator.hasNext()) {
                    FieldAttrEntity fieldAttrEntity = iterator.next();
                    handleAttr4RedOther(xbbRefTypeEnum, unableEditAttrList, redOtherUpdateMap, fieldAttrEntity, memo, itemList);
                }
                break;
            default:
                break;
        }
        return explainList;
    }

    /**
     * 新建or编辑时字段禁止修改处理
     * @param unableEditAttrList 新建or编辑时禁止修改的字段list
     * @param fieldAttrEntity 字段实体
     * @param attr 字段attr
     * @param sheetTypeAttr 回款单/付款单/其他收入单/其他支出单类型字段attr
     * @param memo 类型描述
     * @param itemList 类型集合
     * @author zcp
     * @date 19/11/19 019 14:54
     * @update 19/11/19 019 14:54
     * @since v1.0
     * @version v1.0
     */
    private void handleAttr4Editable(List<String> unableEditAttrList, FieldAttrEntity fieldAttrEntity, String attr, String sheetTypeAttr, String memo, List<ItemPoJo> itemList) {
        if (unableEditAttrList.contains(attr)) {
            fieldAttrEntity.setEditable(0);
        }
        //判断是否是回/付款单/其他收入单/其他支出单类型字段，如果是，则设置其描述文案
        if (Objects.equals(attr, sheetTypeAttr)) {
            fieldAttrEntity.setMemo(memo);
            fieldAttrEntity.setItems(itemList);
        }
    }

    /**
     * 预收款核销字段处理
     * @param xbbRefTypeEnum 回款or付款
     * @param unableEditAttrList 新建or编辑时禁止修改的字段list
     * @param removeAttrList 预收款核销要移除的字段，除了要移除部分系统字段，还需要【移除自定义字段】
     * @param updateMap attr-要更新的字段枚举
     * @param iterator 字段（list或map的）迭代器
     * @param fieldAttrEntity 字段实体
     * @param memo 回款单类型描述
     * @param itemList 回款单类型集合
     * @return void
     * @author zcp
     * @date 19/11/19 019 14:36
     * @update 19/11/19 019 14:36
     * @since v1.0
     * @version v1.0
     */
    private void handleAttr4WriteOffPrepayment(XbbRefTypeEnum xbbRefTypeEnum, List<String> unableEditAttrList, List<String> removeAttrList, Map<String, SheetAssembleEnum.WriteOffPrepayment> updateMap, Iterator iterator, FieldAttrEntity fieldAttrEntity, String memo, List<ItemPoJo> itemList) {
        String attr = fieldAttrEntity.getAttr();
        boolean removeFlag = removeAttrList.contains(attr) || Objects.equals(fieldAttrEntity.getIsRedundant(), 1);
        if (removeFlag) {
            iterator.remove();
        } else {
            //放到return之前，因为编辑时禁用的字段比较多（新建不需要更新，但编辑时需要处理）
            String sheetTypeAttr = Objects.equals(xbbRefTypeEnum, XbbRefTypeEnum.PAY_SHEET) ? PaySheetEnum.PAY_SHEET_TYPE.getAttr() : PaymentSheetEnum.PAYMENT_SHEET_TYPE.getAttr();
            handleAttr4Editable(unableEditAttrList, fieldAttrEntity, attr, sheetTypeAttr, memo, itemList);
            SheetAssembleEnum.WriteOffPrepayment cache = updateMap.get(attr);
            if (cache == null) {
                return;
            }
            fieldAttrEntity.setAttrName(cache.getName());
            fieldAttrEntity.setShowType(cache.getShowType());
            fieldAttrEntity.setRequired(cache.getRequired());
        }
    }

    /**
     * 预收款字段处理
     * @param xbbRefTypeEnum 回款or付款
     * @param unableEditAttrList 新建or编辑时禁止修改的字段list
     * @param removeAttrList 预收款要移除的字段
     * @param iterator 字段（list或map的）迭代器
     * @param fieldAttrEntity 字段实体
     * @param memo 回款单类型描述
     * @param itemList 回款单类型集合
     * @author zcp
     * @date 19/11/19 019 14:39
     * @update 19/11/19 019 14:39
     * @since v1.0
     * @version v1.0
     */
    private void handleAttr4Prepayment(XbbRefTypeEnum xbbRefTypeEnum, List<String> unableEditAttrList, List<String> removeAttrList, Iterator iterator, FieldAttrEntity fieldAttrEntity, String memo, List<ItemPoJo> itemList) {
        String attr = fieldAttrEntity.getAttr();
        if (removeAttrList.contains(attr)) {
            iterator.remove();
        } else {
            String sheetTypeAttr = Objects.equals(xbbRefTypeEnum, XbbRefTypeEnum.PAY_SHEET) ? PaySheetEnum.PAY_SHEET_TYPE.getAttr() : PaymentSheetEnum.PAYMENT_SHEET_TYPE.getAttr();
            handleAttr4Editable(unableEditAttrList, fieldAttrEntity, attr, sheetTypeAttr, memo, itemList);
        }
    }

    /**
     * 红冲已核销，红冲预收款核销字段处理
     * @param xbbRefTypeEnum 回款or付款
     * @param unableEditAttrList 新建or编辑时禁止修改的字段list
     * @param removeAttrList 预收款要移除的字段
     * @param iterator 字段（list或map的）迭代器
     * @param redUpdateMap attr-要更新的字段枚举
     * @param fieldAttrEntity 字段实体
     * @param memo 回款单类型描述
     * @param itemList 回款单类型集合
     * @author zcp
     * @date 19/11/19 019 14:43
     * @update 19/11/19 019 14:43
     * @since v1.0
     * @version v1.0
     */
    private void handleAttr4RedWriteOff(XbbRefTypeEnum xbbRefTypeEnum, List<String> unableEditAttrList, List<String> removeAttrList, Iterator iterator, Map<String, SheetAssembleEnum.RedWriteOff> redUpdateMap, FieldAttrEntity fieldAttrEntity, String memo, List<ItemPoJo> itemList) {
        String sheetTypeAttr = PaymentSheetEnum.PAYMENT_SHEET_TYPE.getAttr();
        String attr = fieldAttrEntity.getAttr();
        if (removeAttrList.contains(attr)) {
            iterator.remove();
            return;
        }
        boolean payFlag = false;
        if (Objects.equals(xbbRefTypeEnum, XbbRefTypeEnum.PAY_SHEET)) {
            sheetTypeAttr = PaySheetEnum.PAY_SHEET_TYPE.getAttr();
            payFlag = true;
        }
        //放到return之前，因为编辑时禁用的字段比较多（新建不需要更新，但编辑时需要处理）
        handleAttr4Editable(unableEditAttrList, fieldAttrEntity, attr, sheetTypeAttr, memo, itemList);
        SheetAssembleEnum.RedWriteOff cache = redUpdateMap.get(attr);
        if (cache == null) {
            return;
        }
        String attrName = payFlag ? cache.getNamePay() : cache.getName();
        fieldAttrEntity.setShowType(cache.getShowType());
        fieldAttrEntity.setAttrName(attrName);
    }
    /**
     * 红冲预收款核销(退到余额)字段处理
     * @param xbbRefTypeEnum 回款or付款
     * @param unableEditAttrList 新建or编辑时禁止修改的字段list
     * @param removeAttrList 预收款要移除的字段
     * @param updateMap attr-要更新的字段枚举
     * @param iterator 字段（list或map的）迭代器
     * @param fieldAttrEntity 字段实体
     * @param memo 回款单类型描述
     * @param itemList 回款单类型集合
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private void handleAttr4RedWriteOffBalance(XbbRefTypeEnum xbbRefTypeEnum, List<String> unableEditAttrList, List<String> removeAttrList, Map<String, SheetAssembleEnum.RedWriteOffBalance> updateMap, Iterator iterator, FieldAttrEntity fieldAttrEntity, String memo, List<ItemPoJo> itemList) {
        String attr = fieldAttrEntity.getAttr();
        boolean removeFlag = removeAttrList.contains(attr);
        if (removeFlag) {
            iterator.remove();
        } else {
            //放到return之前，因为编辑时禁用的字段比较多（新建不需要更新，但编辑时需要处理）
            String sheetTypeAttr = Objects.equals(xbbRefTypeEnum, XbbRefTypeEnum.PAY_SHEET) ? PaySheetEnum.PAY_SHEET_TYPE.getAttr() : PaymentSheetEnum.PAYMENT_SHEET_TYPE.getAttr();
            handleAttr4Editable(unableEditAttrList, fieldAttrEntity, attr, sheetTypeAttr, memo, itemList);
            SheetAssembleEnum.RedWriteOffBalance cache = updateMap.get(attr);
            if (cache == null) {
                return;
            }
            fieldAttrEntity.setRequired(cache.getRequired());
            fieldAttrEntity.setAttrName(cache.getName());
            fieldAttrEntity.setShowType(cache.getShowType());
        }
    }
    /**
     * 红冲预收款字段处理
     * @param xbbRefTypeEnum 回款or付款
     * @param unableEditAttrList 新建or编辑时禁止修改的字段list
     * @param removeAttrList 预收款要移除的字段
     * @param iterator 字段（list或map的）迭代器
     * @param redPrepaymentUpdateMap attr-要更新的字段枚举
     * @param fieldAttrEntity 字段实体
     * @param memo 回款单类型描述
     * @param itemList 回款单类型集合
     * @author zcp
     * @date 19/11/19 019 14:46
     * @update 19/11/19 019 14:46
     * @since v1.0
     * @version v1.0
     */
    private void handleAttr4RedPrepayment(XbbRefTypeEnum xbbRefTypeEnum, List<String> unableEditAttrList, List<String> removeAttrList, Iterator iterator, Map<String, SheetAssembleEnum.RedPrepayment> redPrepaymentUpdateMap, FieldAttrEntity fieldAttrEntity, String memo, List<ItemPoJo> itemList) {
        String attr = fieldAttrEntity.getAttr();
        if (removeAttrList.contains(attr)) {
            iterator.remove();
        } else {
            String sheetTypeAttr = Objects.equals(xbbRefTypeEnum, XbbRefTypeEnum.PAY_SHEET) ? PaySheetEnum.PAY_SHEET_TYPE.getAttr() : PaymentSheetEnum.PAYMENT_SHEET_TYPE.getAttr();
            //放到return之前，因为编辑时禁用的字段比较多（新建不需要更新，但编辑时需要处理）
            handleAttr4Editable(unableEditAttrList, fieldAttrEntity, attr, sheetTypeAttr, memo, itemList);
            SheetAssembleEnum.RedPrepayment cache = redPrepaymentUpdateMap.get(attr);
            if (cache == null) {
                return;
            }
            fieldAttrEntity.setShowType(cache.getShowType());
            fieldAttrEntity.setAttrName(cache.getName());
        }
    }

    /**
     * 坏账字段处理
     * @param xbbRefTypeEnum 回款or付款
     * @param unableEditAttrList 新建or编辑时禁止修改的字段list
     * @param removeAttrList 预收款要移除的字段
     * @param iterator 字段（list或map的）迭代器
     * @param badUpdateMap attr-要更新的字段枚举
     * @param fieldAttrEntity 字段实体
     * @param memo 回款单类型描述
     * @param itemList 回款单类型集合
     * @author zcp
     * @date 19/11/19 019 14:49
     * @update 19/11/19 019 14:49
     * @since v1.0
     * @version v1.0
     */
    private void handleAttr4BadPayment(XbbRefTypeEnum xbbRefTypeEnum, List<String> unableEditAttrList, List<String> removeAttrList, Iterator iterator, Map<String, SheetAssembleEnum.BadPayment> badUpdateMap, FieldAttrEntity fieldAttrEntity, String memo, List<ItemPoJo> itemList) {
        String attr = fieldAttrEntity.getAttr();
        if (removeAttrList.contains(attr)) {
            iterator.remove();
        } else {
            String sheetTypeAttr = PaymentSheetEnum.PAYMENT_SHEET_TYPE.getAttr();
            boolean payFlag = false;
            if (Objects.equals(xbbRefTypeEnum, XbbRefTypeEnum.PAY_SHEET)) {
                sheetTypeAttr = PaySheetEnum.PAY_SHEET_TYPE.getAttr();
                payFlag = true;
            }
            //放到return之前，因为编辑时禁用的字段比较多（新建不需要更新，但编辑时需要处理）
            handleAttr4Editable(unableEditAttrList, fieldAttrEntity, attr, sheetTypeAttr, memo, itemList);
            SheetAssembleEnum.BadPayment cache = badUpdateMap.get(attr);
            if (cache == null) {
                return;
            }
            String attrName = payFlag ? cache.getNamePay() : cache.getName();
            fieldAttrEntity.setAttrName(attrName);
            fieldAttrEntity.setShowType(cache.getShowType());
        }
    }

    /**
     * 红冲其他收入单/其他支出单，即红冲已收款/红冲已付款字段处理
     * @param xbbRefTypeEnum 其他收入单or其他支出单
     * @param unableEditAttrList 新建or编辑时禁止修改的字段list
     * @param redUpdateMap attr-要更新的字段枚举
     * @param fieldAttrEntity 字段实体
     * @param memo 类型描述
     * @param itemList 类型集合
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private void handleAttr4RedOther(XbbRefTypeEnum xbbRefTypeEnum, List<String> unableEditAttrList, Map<String, SheetAssembleEnum.RedOther> redUpdateMap, FieldAttrEntity fieldAttrEntity, String memo, List<ItemPoJo> itemList) {
        String sheetTypeAttr = OtherIncomeEnum.SHEET_TYPE_OTHER.getAttr();
        String attr = fieldAttrEntity.getAttr();
        boolean expenseFlag = false;
        if (Objects.equals(xbbRefTypeEnum, XbbRefTypeEnum.OTHER_EXPENSE)) {
            sheetTypeAttr = OtherExpenseEnum.SHEET_TYPE_OTHER.getAttr();
            expenseFlag = true;
        }
        //放到return之前，因为编辑时禁用的字段比较多（新建不需要更新，但编辑时需要处理）
        handleAttr4Editable(unableEditAttrList, fieldAttrEntity, attr, sheetTypeAttr, memo, itemList);
        SheetAssembleEnum.RedOther cache = redUpdateMap.get(attr);
        if (cache == null) {
            return;
        }
        String attrName = expenseFlag ? cache.getNameExpense() : cache.getName();
        fieldAttrEntity.setShowType(cache.getShowType());
        fieldAttrEntity.setAttrName(attrName);
    }


    public void setInvoiceNotEditToExplain( List<FieldAttrEntity> explains,Integer fromRed,Integer isRed,Integer businessType){
        if(Objects.equals(businessType,XbbRefTypeEnum.INVOICE.getCode())) {
            //if (Objects.equals(fromRed, BasicConstant.ONE) || Objects.equals(isRed, BasicConstant.ONE)) {
                explains.forEach(fieldAttrEntity -> {
                    if (Objects.equals(InvoiceEnum.PAYMENT_RELATIONSHIP.getAttr(), fieldAttrEntity.getAttr())
                            || Objects.equals(InvoiceEnum.CONTRACT_ID.getAttr(), fieldAttrEntity.getAttr())
                            || Objects.equals(InvoiceEnum.PAYMENT_ID.getAttr(), fieldAttrEntity.getAttr())
                            || Objects.equals(InvoiceEnum.PAYMENT_SHEET_ID.getAttr(), fieldAttrEntity.getAttr())) {
                        fieldAttrEntity.setEditable(0);
                    }
                    //放开开票日期，让他可以编辑
                    if(Objects.equals(InvoiceEnum.TOTAL_MONEY.getAttr(), fieldAttrEntity.getAttr())){
                        if(!Objects.equals(fromRed,BasicConstant.ONE) || Objects.equals(isRed, BasicConstant.ONE)){
                            fieldAttrEntity.setEditable(0);
                        }
                    }
                });
            //}
        }else if(Objects.equals(businessType,XbbRefTypeEnum.PURCHASE_INVOICE.getCode())){
            //if(Objects.equals(isRed,BasicConstant.ONE) || Objects.equals(fromRed,BasicConstant.ONE)){
                explains.forEach(fieldAttrEntity -> {
                    if(Objects.equals(PurchaseInvoiceEnum.PAY_RELATIONSHIP.getAttr(),fieldAttrEntity.getAttr())
                            || Objects.equals(PurchaseInvoiceEnum.PURCHASE_ID.getAttr(),fieldAttrEntity.getAttr())
                            || Objects.equals(PurchaseInvoiceEnum.PAY_PLAN_ID.getAttr(),fieldAttrEntity.getAttr())
                            || Objects.equals(PurchaseInvoiceEnum.PAY_SHEET_ID.getAttr(),fieldAttrEntity.getAttr())
                            || Objects.equals(PurchaseInvoiceEnum.INVOICE_DATE.getAttr(),fieldAttrEntity.getAttr())
                            || Objects.equals(PurchaseInvoiceEnum.TOTAL_MONEY.getAttr(),fieldAttrEntity.getAttr())){
                        fieldAttrEntity.setEditable(0);
                    }
                    if(Objects.equals(PurchaseInvoiceEnum.INVOICE_DATE.getAttr(),fieldAttrEntity.getAttr())
                            ||Objects.equals(PurchaseInvoiceEnum.TOTAL_MONEY.getAttr(),fieldAttrEntity.getAttr())){
                        if(!Objects.equals(fromRed,BasicConstant.ONE) || Objects.equals(isRed, BasicConstant.ONE)){
                            fieldAttrEntity.setEditable(0);
                        }
                    }
                });
            //}
        }
    }

    public Boolean checkInvoiceByCustomerOrNot(JSONObject data,Integer businessType){
        Boolean flag = false;
        if(Objects.equals(businessType,XbbRefTypeEnum.INVOICE.getCode())){
            JSONArray contract = data.getJSONArray(InvoiceEnum.CONTRACT_ID.getAttr());
            JSONArray payment = data.getJSONArray(InvoiceEnum.PAYMENT_ID.getAttr());
            JSONArray paymentSheet = data.getJSONArray(InvoiceEnum.PAYMENT_SHEET_ID.getAttr());
            if(CollectionsUtil.isEmpty(contract) && CollectionsUtil.isEmpty(payment) && CollectionsUtil.isEmpty(paymentSheet)){
                flag = true;
            }
            //这边针对关联合同特殊处理，当发票关联了合同也认为他是从客户来的
            if(CollectionsUtil.isNotEmpty(contract) && CollectionsUtil.isEmpty(payment) && CollectionsUtil.isEmpty(paymentSheet)){
                flag = true;
            }
        }
        return flag;
    }

    /**
     * 处理资金账户中的类型
     *
     * @param type 类型
     * @param corpid 公司id
     * @return
     */
    public List<Integer> dealWithFundType(Integer type, String corpid) {
        if (Objects.equals(type, FundAccountFlowTypeEnum.ALL.getCode())) {
            return FundAccountFlowTypeEnum.getSearchTypes();
        }else if (Objects.equals(type, FundAccountFlowTypeEnum.FUND_TRANSFER_IN.getCode())) {
            return Arrays.asList(FundAccountFlowTypeEnum.FUND_TRANSFER_IN.getCode(), FundAccountFlowTypeEnum.FUND_TRANSFER_OUT.getCode());
        }else {
            FundAccountFlowTypeEnum fundAccountFlowTypeEnum = FundAccountFlowTypeEnum.getAccountTypeByCode(type);
            return Arrays.asList(fundAccountFlowTypeEnum.getCode());
        }
    }

    /**
     * 获取生成对账单对账单中应收款单据的id和回款类型的map
     * @param list  对张单据的集合
     * @param corpid 公司id
     */
    public Map<Long, String> getPaymentIdMapForStatementData(List<CustomerStatementEntity> list, String corpid ,Map<Object, String> itemsMap) throws XbbException{
        List<Long> paymentIdList = new ArrayList<>();
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        // 限制返回值
        List<String> partFieldList = Arrays.asList("data." + PaymentEnum.PAYMENT_TYPE.getAttr(),"id");
        Map<Long, String> paymentIdToType = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        //获取应收款单据idList
        for (CustomerStatementEntity entity : list) {
            Long statementId = entity.getSysLong2();
            statementId = statementId != null ? statementId : 0L;
            paymentIdList.add(statementId);
        }
        boolQueryBuilder.filter(termQuery(ParameterConstant.CORPID + StringConstant.POINT + StringConstant.KEY_WORD,corpid));
        boolQueryBuilder.filter(termsQuery("id",paymentIdList));
        //应收款查询条件
        List<PaymentEntityExt> paymentEntityExts = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_PAYMENT, boolQueryBuilder, PaymentEntityExt.class,partFieldList);
        if (CollectionsUtil.isNotEmpty(paymentEntityExts)){
            for (PaymentEntityExt entity : paymentEntityExts){
                String paymentType = FastJsonHelper.getStringOrDefaultFromFormData(entity.getData(), PaymentEnum.PAYMENT_TYPE.getAttr(), "");
                String paymentTypeItem = Objects.equals(itemsMap.getOrDefault(paymentType, ""),"") ? paymentType : itemsMap.getOrDefault(paymentType, "");
                paymentIdToType.put(entity.getId(),paymentTypeItem);
            }
        }
        return paymentIdToType;
    }

    /**
     * 获取对账单列表中应收款单据的id和回款类型的map
     * @param list  对账单列表单据集合
     * @param corpid  公司id
     * @return
     * @throws XbbException
     */
    public Map<Long, String> getPaymentIdMapForStatementStatisticData(List<CustomerStatementSnapshotEntity> list,  String corpid ,Map<Object, String> itemsMap) throws XbbException{
        List<Long> paymentIdList = new ArrayList<>();
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        // 限制返回值
        List<String> partFieldList = Arrays.asList("data." + PaymentEnum.PAYMENT_TYPE.getAttr(),"id");
        Map<Long, String> paymentIdToType = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        //获取应收款单据idList
        for (PaasFormDataEntity entity : list) {
            Long statementId = entity.getData().getLong(CustomerStatementSnapshotEnum.STATEMENT_ID.getAttr());
            statementId = statementId != null ? statementId : 0L;
            paymentIdList.add(statementId);
        }
        boolQueryBuilder.filter(termQuery(ParameterConstant.CORPID + StringConstant.POINT + StringConstant.KEY_WORD,corpid));
        boolQueryBuilder.filter(termsQuery("id",paymentIdList));
        //应收款查询条件
        List<PaymentEntityExt> paymentEntityExts = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_PAYMENT, boolQueryBuilder, PaymentEntityExt.class,partFieldList);
        if (CollectionsUtil.isNotEmpty(paymentEntityExts)){
            for (PaymentEntityExt entity : paymentEntityExts){
                String paymentType = FastJsonHelper.getStringOrDefaultFromFormData(entity.getData(), PaymentEnum.PAYMENT_TYPE.getAttr(), "");
                String paymentTypeItem = Objects.equals(itemsMap.getOrDefault(paymentType, ""),"") ? paymentType : itemsMap.getOrDefault(paymentType, "");
                paymentIdToType.put(entity.getId(),paymentTypeItem);
            }
        }
        return paymentIdToType;
    }

    /**
     *  为了获取应收款 -> "回款类型" 下拉框里面的值 ，得从应收款解释里面获取
     * @param corpid
     * @param distributorMark
     * @return
     */
    public Map<Object, String> getItemsFromPaymentExplain(String corpid ,Integer distributorMark) throws XbbException {
        Map<Object, String> itemsMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        PaasFormExplainEntity paasFormExplainEntity = paasFormExplainModel.getByBusinessType4Distributor(XbbRefTypeEnum.PAYMENT.getCode(), corpid, distributorMark);
        if (Objects.nonNull(paasFormExplainEntity)){
            List<FieldAttrEntity> fieldList = JsonHelperUtil.parseArray(paasFormExplainEntity.getExplains(), FieldAttrEntity.class);
            if (CollectionsUtil.isNotEmpty(fieldList)){
                for (FieldAttrEntity fieldAttrEntity : fieldList){
                    if (Objects.equals(fieldAttrEntity.getAttr(),PaymentEnum.PAYMENT_TYPE.getAttr())){
                        List<ItemPoJo> items = fieldAttrEntity.getItems();
                        items.forEach(itemPoJo -> {
                            itemsMap.put(itemPoJo.getValue(),itemPoJo.getText());
                        });
                        break;
                    }
                }
            }
        }
        return itemsMap;
    }

    /**
     * 资金调拨单 - 校验调拨日期
     * @param corpid 公司id
     * @param date 日期
     * @return
     * @throws XbbException
     */
    public boolean checkTransferDate(String corpid,Long date) throws XbbException {
        FundAccountFlowEntity flowEntity = fundAccountFlowModel.getLatestFlowEntity(corpid, date);
        return Objects.nonNull(flowEntity);
    }
}
